Terraform vs Terragrunt vs Terraspace
Terraform is great and helps you build infrastructure-as-code. The Hashicorp team provides good resources, learning guides, and tutorials to help you get started. The guides are simple, though, and still leave plenty of things for you to figure out on our own. Once you get past the introductory basics of Terraform, you’ll discover many more questions that you must answer before taking Terraform to production. Usually, people end up home-growing custom scripts or using a tool on top of Terraform to fill the gaps. In this article, we’ll discuss and compare the tools: Terraform, Terragrunt, and Terraspace.
Why Not Pure Terraform?
First, let’s discuss why people choose to use a tool with Terraform vs. just using pure Terraform.
You see, Terraform merely provides the basic building blocks or raw materials for construction. The HCL language is like wood. Terraform also provides some fantastic cloud providers. Those are like drill adapters for your tools. After that, you’re on your own.
Terraform is also rather unopinionated. It provides some recommendations, but it doesn’t provide strong instructions. In some cases, being unopinionated is beneficial, but it can also serve as a hindrance. Sometimes, in life, a little commitment can go a long way.
Terraform vs Terragrunt
Terragrunt is a thin Terraform wrapper that provides tooling and some opinions. Instead of calling terraform directly, you call terragrunt commands, which, in turn, call the corresponding terraform commands. For example:
terragrunt apply => terraform apply
terragrunt destroy => terraform destroy
By wrapping terraform commands, terragrunt can perform some beneficial logic before and after the terraform calls. The benefits include:
- Some recommendations for a project structure.
- A way keep your code DRY: You do this by using the generate helper method.
- Automated creation of backends. It isn’t very pleasant to have to create the backend bucket manually.
- CLI hooks: Allows you to execute custom actions before or after the terraform commands.
- Ability to declare dependencies and deploy all or multiple modules with terragrunt apply-all.
Given that DevOps space is at a relatively young phase, tools like Terragrunt have done a fantastic job. People use tools like Terragrunt because it makes life easier and better. You don’t have to implement and build these benefits yourself.
Terraspace Improves Life Further
Terraspace supports all the aforementioned Terragrunt benefits - and improves on them.
- An organized project structure.
- Keeping your code DRY by building Terraform projects from your
app
andconfig/terraform
files. - The automated creation of backend buckets.
- CLI hooks: Call custom commands before and after terraform commands.
- You can deploy all or some infrastructure in a single command: terraspace all up.
Let’s talk about the improvements in turn.
Project Structure
With Terragrunt, you must carefully plan your structure. This is still one of the most discussed, debated, and blogged about topics when getting started with Terraform. Simply Google it. It’s a lot of thinking you must do before you can even get started.
With Terraspace, you don’t have to spend a week upfront thinking about the project structure. It’s done.
It’s also configurable. Since Terraspace dynamically builds Terraform projects, you can change the locations. The conventions can be overridden. You still have the full power to configure things per your requirements. The default structure already accounts for 80% of use cases, though. Out of the gates, you can already use the same code to create different environments like dev and prod. You can even deploy them to multiple regions. Again, with the same code.
Really DRY
Terragrunt says it’s DRY, but it’s terragrunt.hcl
configurations are duplicated. Here’s a typical terragrunt project structure:
us-east-1
├── dev
│ ├── mysql
│ │ └── terragrunt.hcl
│ ├── vpc
│ │ └── terragrunt.hcl
│ └── webserver-cluster
│ └── terragrunt.hcl
├── prod
│ ├── mysql
│ │ └── terragrunt.hcl
│ ├── vpc
│ │ └── terragrunt.hcl
│ └── webserver-cluster
│ └── terragrunt.hcl
└── terragrunt.hcl
So what happens if you need to create another environment like stage?
You have to copy and paste the terragrunt.hcl
files like so:
us-east-1
├── dev
│ ├── mysql
│ │ └── terragrunt.hcl
│ ├── vpc
│ │ └── terragrunt.hcl
│ └── webserver-cluster
│ └── terragrunt.hcl
├── prod
│ ├── mysql
│ │ └── terragrunt.hcl
│ ├── vpc
│ │ └── terragrunt.hcl
│ └── webserver-cluster
│ └── terragrunt.hcl
├── stage
│ ├── mysql
│ │ └── terragrunt.hcl
│ ├── vpc
│ │ └── terragrunt.hcl
│ └── webserver-cluster
│ └── terragrunt.hcl
└── terragrunt.hcl
Then what happens when you have to go multi-regional?
You end up copying and pasting the regional folders. There’s even more duplication:
├── us-east-1
│ ├── dev
│ │ ├── mysql
│ │ │ └── terragrunt.hcl
│ │ ├── vpc
│ │ │ └── terragrunt.hcl
│ │ └── webserver-cluster
│ │ └── terragrunt.hcl
│ ├── prod
│ │ ├── mysql
│ │ │ └── terragrunt.hcl
│ │ ├── vpc
│ │ │ └── terragrunt.hcl
│ │ └── webserver-cluster
│ │ └── terragrunt.hcl
│ ├── stage
│ │ ├── mysql
│ │ │ └── terragrunt.hcl
│ │ ├── vpc
│ │ │ └── terragrunt.hcl
│ │ └── webserver-cluster
│ │ └── terragrunt.hcl
│ └── terragrunt.hcl
└── us-west-2
├── dev
│ ├── mysql
│ │ └── terragrunt.hcl
│ ├── vpc
│ │ └── terragrunt.hcl
│ └── webserver-cluster
│ └── terragrunt.hcl
├── prod
│ ├── mysql
│ │ └── terragrunt.hcl
│ ├── vpc
│ │ └── terragrunt.hcl
│ └── webserver-cluster
│ └── terragrunt.hcl
├── stage
│ ├── mysql
│ │ └── terragrunt.hcl
│ ├── vpc
│ │ └── terragrunt.hcl
│ └── webserver-cluster
│ └── terragrunt.hcl
└── terragrunt.hcl
Good luck updating all of those terragrunt.hcl
files.
With Terraspace, the Terraform projects are generated. There are no duplicated configuration files. If you need to create another environment, use the TS_ENV
environment variable.
TS_ENV=dev terraspace up vpc
TS_ENV=prod terraspace up vpc
If you need to go to multiple regions, use the AWS_REGION environment variable.
AWS_REGION=us-east-1 TS_ENV=dev terraspace up vpc
AWS_REGION=us-west-2 TS_ENV=prod terraspace up vpc
Life’s easier when there’s less duplication.
Backend Bucket Creation Automation
Terraspace not only automatically create the backend buckets for you, but it also has more configurable knobs and switches. For the AWS S3 backend, you can enable encryption, enforce an SSL bucket policy, enable versioning, turn on lifecycle policies, and log bucket server access. All of which are configurable:
TerraspacePluginAws.configure do |config|
config.auto_create = true # set to false to completely disable auto creation
config.s3.encryption = true
config.s3.enforce_ssl = true
config.s3.versioning = true
config.s3.lifecycle = true
config.s3.access_logging = false # false is the default setting
config.s3.secure_existing = false # run the security controls on existing buckets. by default, only run on newly created bucket the first time
config.dynamodb.encryption = true
config.dynamodb.kms_master_key_id = nil
config.dynamodb.sse_type = "KMS"
end
Additionally, all 3 major cloud provider backends are supported out of the box: aws, azure, google. Terragrunt does not support the automated creation of buckets for azure.
Terraspace has its own plugin architecture that makes this all possible. You can create your own provider plugin if you want. There’s even a plugin generator.
CLI Hooks and Args Customizations
Thanks to the expressiveness of Ruby, the Terraspace CLI hooks syntax is cleaner.
before("init",
execute: "echo hi",
)
after("apply",
execute: "echo bye"
)
It’s also more configurable. If we want Terraspace to continue when the hook fails, it’s configurable:
before("init",
execute: "/command/will/fail/but/will/continue",
exit_on_fail: false,
)
Customizing terraform args is also more beautiful. We can specify multiple commands concisely.
command("init", "apply",
args: ["-lock-timeout=20m"],
env: {TF_VAR_var_from_environment: "value"},
)
There are handy shorthands like :with_vars
also:
commands(:with_vars,
args: ["-lock-timeout=20m"],
var_files: ["a.tfvars", "b.tfvars"],
)
Life’s better when code is more readable.
Deploy All Stacks or Terraform Modules
Being able to deploy all of your stacks together with a single command is a wonderfully powerful feature. Just the minimal ability alone is not enough, though. How friendly it is to use also matters.
The terragrunt apply-all
command streams all the output from multiple terraform apply
processes running in parallel to stdout. With logs, it is possible to have too much of a good thing. It’s annoying to see all the terraform apply
output in one huge stream. It’s a big mess and hard to read. You’ll end up using sed
and awk
to clean up the logging output, or writing your own filtering scripts, maybe even another wrapper script. Humans need filtered, readable output.
Terraspace provides a summarized reduced-noise output that is clean. The output provides a high-level overview of what is happening and helps you make sense of the output. At the same time, Terraspace saves the full log files for further inspection and debugging.
Dependencies in Terraspace are also less work to define. Moreover, they are decoupled from the code structure. It encourages composition and reusability, which is what Terraform officially recommends: Module Composition.
Terraspace’s ability to deploy all or multiple stacks is extremely flexible, feature-rich, and more powerful.
Terraspace is Terraform on Steroids
We’re not done yet. On top of the existing features just being easier to use, Terraspace also provides a slew of extra benefits.
Major Clouds Supported
Out of the gates, the 3 major cloud providers are supported: aws, azure, google. There are also easy and gentle learning guides for each of them:
TFC and TFE Supported
Terraform Cloud and Terraform Enterprise are also supported. Both CLI and VCS driven workflows are supported:
Getting any tool to work with TFC requires effort. Terraspace can automatically set up the workspace GitHub connection, and things like environment vars. It simply automates as much as it can.
If you’re on TFE and have limited permissions, you can even ask your admin to explicitly run terraspace cloud sync to create the workspaces in batch. This saves you hours of time manually creating the workspaces, which is also error-prone.
Docs: TFC/TFE support.
Generators: Stop Writing Boilerplate
Your time is precious. Why are we copying and pasting boilerplate structure in this day and age?
Terraspace provides generators to help you get going right away. There are generators for projects, stacks, modules, tests, shims, git hooks, even plugins creation.
Docs: Generators
The Power of Layering
Terraspace Layering provides incredible power at your disposal. It’s possible to use the same code to deploy and create multiple environments across multiple regions with just environment variables. No extra configuration structure duplication is needed.
Deploying All or Subgraphs
We mentioned earlier that Terraspace allows you to deploy all of your infrastructure with a single command.
terraspace all up
Terraspace also allows you to target any subgraph of your infrastructure you wish. You simply provide the list of stacks, and Terraspace does the heavy lifting. The graphs don’t even have to be connected.
terraspace all up stack1 stack2
You can also produce beautiful graphs at the flick of the wrist. You can build filtered subgraphs that are highlighted with different colors to help visualize and understand your infrastructure. Multiple members on your team will appreciate it.
Docs: Deploying All or Multiple Stacks and Subgraphs
Terraspace Logs
Terrspace output is summarized to reduce noise for human beings. The full detailed logs are also written to files for further inspection. On top of that, Terraspace provides a terraspace logs command to stream and quickly view the logs with convenient and smart defaults.
Use the terraspace logs -f
command to stream the full output if you want to see all of it in its full glory. New logs files are automatically detected and added to be followed.
If you only want to see the last run’s logs, you can drill in by specifying the action and stack. Here’s an example viewing the demo stack’s logs: terraspace logs up demo
Docs: Deploy Example and terraspace logs.
Terrafile: Use Any Module You Want
Terraspace makes it easy to use Terraform modules sourced from your own git repositories, other git repositories, or the Terraform Registry. The git repos can be private or public. This is an incredibly powerful feature of Terraspace because it opens up a world of modules for you to use.
Docs: Terrafile Docs
Built-in Test Framework
Terraspace ships with a built-in Test Framework. The test framework allows you to use a language for testing that is readable and clear: Ruby and Rspec. Terraspace actually dogfoods itself by using its own test framework. All 3 major cloud providers are tested before a new release: aws, azure, google. We’re talking with about testing with real resources.
Docs: Built-in Test Framework:
Configurable Behavior
An abundant number of framework settings and behaviors can be adjusted. There are more than 20 settings listed on the config reference docs. And these are only the core framework settings we’re talking about here. If we add settings for the Terraspace provider plugins, CLI hooks, and Customizable Args, which are also configurable, the list grows even longer. Terraspace provides reasonable defaults, but it’s very configurable in case you need more control.
Docs: Configurable Framework Behavior:
Native Terraform HCL with Ruby Sprinkles
With Terraspace, you use native Terraform HCL syntax. There is no additional custom HCL syntax to learn. Terraspace sticks to native HCL syntax, tfvars files, and works with whatever Terraform supports. Terraspace doesn’t conflate a custom HCL syntax with the native Terraform HCL. You also have access to Ruby, but it’s lightly added on top of Terraform. Think about it as “Ruby Sprinkles.”
The Power of Ruby
Terraspace is written in Ruby. This fact is transparent to the end-user. The starter learning guides all take you through a gentle path, where you are using Terraform HCL just like you usually would.
Little do you know, even though you don’t see much Ruby, you have the full power of Ruby at your fingertips. Ruby is a full programming language and one of the most beautiful and expressive languages in the world. Its versatility makes it adept for writing glue and frameworks.
Modern-day DevOps shops use a variety of tools like bash, python, ruby, and go to achieve their goals. As the adage goes: use the right tool for the job. Language shouldn’t matter, but it does. Ruby is one of the most powerful languages to craft tools and frameworks like Terraspace to make your life easier.
Wrapping Up
In this post, we discuss why folks end up using tools like Terragrunt and Terraspace instead of Terraform alone. Tools provide many benefits for free. You don’t have to implement and reinvent the wheel yourself.
We’ve also pointed out many differences between Terraform, Terragrunt, and Terraspace. At the end of the day, Terraspace is a full-fledged framework. Whereas, Terragrunt is a thin wrapper. We’re really comparing apples to oranges here.
At this point, if you’re still wondering what’s right for you. Think that theorizing will only get you so far. Building a small test proof-of-concept project with each tool is sometimes the fastest path to reaching a decision. So go out there and start building!
Related Posts
You may also be interested in:
Thanks for reading this far. If you found this article useful, I'd really appreciate it if you share this article so others can find it too! Thanks 😁 Also follow me on Twitter.
Got questions? Check out BoltOps.
You might also like
More tools:
-
Kubes
Kubes: Kubernetes Deployment Tool
Kubes is a Kubernetes Deployment Tool. It builds the docker image, creates the Kubernetes YAML, and runs kubectl apply. It automates the deployment process and saves you precious finger-typing energy.
-
Jets
Jets: The Ruby Serverless Framework
Ruby on Jets allows you to create and deploy serverless services with ease, and to seamlessly glue AWS services together with the most beautiful dynamic language: Ruby. It includes everything you need to build an API and deploy it to AWS Lambda. Jets leverages the power of Ruby to make serverless joyful for everyone.
-
Lono
Lono: The CloudFormation Framework
Building infrastructure-as-code is challenging. Lono makes it much easier and fun. It includes everything you need to manage and deploy infrastructure-as-code.