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.

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.

Docs: Tfvars and Layering

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!

You may also be interested in: