Terraspace cheat sheet

Pier
Geek Culture
Published in
6 min readJan 20, 2022

This is the last article, of a three-episode series on Terragrunt and Terraspace.

In order to really compare the two frameworks, in this blog post, there are multiple parts taken from Episode 2 of Terragrunt.

Episode 1: From Terralith to Terraservice with Terraform
Episode 2:
Terragrunt cheat sheet
Episode 3: Terraspace cheat sheet (this article)

Today I’m presenting a Terraspace project with the following characteristics:

  • Multi-Environment: QA and TEST for each single account
  • Multi-Account: Different accounts Test and Dev
  • Multi-Region: Ready to split accounts and projects between different regions

Obviously, in the end, you should be able to change the requirements accordingly to your needs.

The scope of this overview is to give you the core concept and structure behind Terraspace.
In my public repository, I’ve added some dependencies between modules. The goal is to provide something similar to a real use case.

Repository link:
At the bottom of this article.

[2] — [3]
[1]-[2]

What is Terraspace?

Terraspace allows keeping your code DRY, writing plain terraform files but providing additional features to decrease complexity in managing multiple statefiles.

Repository structure:

Photo by Joel & Jasmin Førestbird on Unsplash

In this project, I’m going to create a VPC and an EC2 instance using:

  • two different modules with dependencies [vpc, ec2]
  • two different accounts [Test, Dev]
  • two different environments for each account [QA, TEST]

One makefile to rule them all, one makefile to find them, one makefile to bring them all, and in the darkness bind them.

terraspace
├── Makefile
└── infrastructure
├── Gemfile
├── Gemfile.lock
├── README.md
├── Terrafile
├── app
│ ├── modules
│ │ ├── ec2
│ │ │ ├── ec2.tf
│ │ │ ├── variables.tf
│ │ │ └── versions.tf
│ │ └── vpc
│ │ ├── main.tf
│ │ ├── output.tf
│ │ ├── variables.tf
│ │ └── versions.tf
│ └── stacks
│ ├── ec2-service
│ │ ├── main.tf
│ │ ├── tfvars
| | | └── eu-central-1
│ │ │ ├── base.tfvars
│ │ │ ├── qa.tfvars
│ │ │ └── test.tfvars
│ │ └── variables.tf
│ └── vpc-service
│ ├── main.tf
│ ├── output.tf
│ ├── tfvars
| | └── eu-central-1
│ │ ├── qa.tfvars
│ │ └── test.tfvars
│ └── variables.tf
└── config
├── app.rb
├── args
│ └── terraform.rb
└── terraform
├── backend.tf
├── provider.tf
└── tfvars
└── eu-central-1
├── base.tfvars
├── qa.tfvars
└── test.tfvars

Let’s start from the app folder where we can identify modules and stacks.

In modules we put all the code that we want to reuse across different services, like “vpc” and “ec2”.

In stacks instead, there are two sub-folders:

  • vpc-service”: Refers to the “vpc” module. It’s used to create the network where we are going to create the ec2 instance.
  • ec2-service”: Refers to the “ec2” module and it has dependencies from the “vpc-service. You need a VPC to create the instance.
  • in both the “x-service” there is a folder tfvars. In it, we put the variables to configure the differences between the two environments.

The config folder is a bit more complex:

infrastructure
└── config
├── app.rb
├── args
│ └── terraform.rb
└── terraform
├── backend.tf
├── provider.tf
└── tfvars
└── eu-central-1
├── base.tfvars
├── qa.tfvars
└── test.tfvars
  • args: it contains some flag that terraspace will pass @ terraform like -lock-timeout=20m [Official doc here]
  • app.rb: not used by me, but it allows to configure test and logger [Official doc here]
  • terraform/backend.tf & provider.tf: configuration for the backend where we’ll store the statefile. Write once, and reuse many is the rule here.
  • terraform/tfvars/eu-central-1: Environment variables considered by terraspace. In the Makefile, we define TS_ENV to control which env-specific tfvars file to layer on top of base.tfvars.

The Terrafile is a file that provides consistency across your repository. to explain it we need a dedicated paragraph.
For the official structure doc, refer here.

How do we solve The 3 “Multi-” pattern?

  • Multi-Environment: Every environment owns a different env_xx.tfvar and we select the correct file setting this environment variable TS_ENV=qa interpreted by terraspace.
  • Multi-Account: Choosing the corrected environment variable to reach the expected account. Done in the “Makefile”.
  • Multi-Region: Ready to split accounts and projects between different regions. You need to copy and rename all the folder that at the moment refers to “eu-central-1”

How does the Multi-Environment feature work?

TS_ENV=”qa” — The magic is here..

With this syntax, we are going to configure terraspace to merge two files that exist in this folder terraspace/infrastructure/app/stacks/ec2-service/tfvars this feature is also called terraspace layering:

  1. base.tfvars
  2. qa.tfvars

Do you want to configure the test environment differently?

Simply export TS_ENV=”test” that will merge:

  1. base.tfvars
  2. test.tfvar

This mechanism is applied everywhere, also for the tfvars shared across all the modules and defined here terraspace/infrastructure/config/terraform/tfvars

Modules…..and stacks?

The difference is only in your design, both may be terraform modules!

  • module: they are closer to the terraform module meaning. They are a group of resources that should live together. A registry and its lifecycle policy are an example. Define a module when you want something, shared across different parts of your IaC repository.
  • stack: here there is your business logic. Which approach are you using? A group for each Cloud provider resource? An example may be all the Database instances together, or maybe your point of view stays at the product level? Here you are referring to the module above.

Terrafile

The terrafile is a tool that simplifies the day-2 operations, providing a centralized place where you define all your versions and definitions of the modules used across your project. You don’t have to use it, but you should!

Moreover, do you know what the role of a lockfile is? It exists in python, GoLang, node.js, and the major part of programming languages… also terraspace has it! It’s a mechanism to enforce cohesion of the modules across different actors like a team and the CI/CD to avoid inconsistency in intra-module dependency.

To run all the commands, in the GitHub repo exist a Makefile to simplify the adoption in the pipelines. More details in the README
You can easily extend it, or create a fresh new bash script!

Repository link:
https://bit.ly/3maXDL0

Pros vs Cons:

++ Strong points:

  • Deploy everything ( but you should be skilled enough to manage all the dependencies, or deploy a single stack and all its dependencies: https://terraspace.cloud/docs/intro/deploy-all/
  • You can share variables between stacksusing the following syntax:
<%= output('vpc-service.vpc_service_vpc_public_subnets') %>
  • The syntax is standard TF, no need to learn a new language (maybe Ruby?). But there are some new commands given by terraspace.
  • You are DRY, but there is a drawback, see below!
  • A different TFState for each stack auto-generated.
  • You can always refer to external modules.
  • Manage different environments using TF_ENV and layering.
TS_ENV=dev  terraspace up vpc
TS_ENV=prod terraspace up vpc

— — Weak points :

  • TFVARS layering in the stacks. It’s a feature that is missing in the vanilla terraform. Use it consciously, it may become a Matryoshka doll.
  • DRY consequence: If you do a change to main.tf inside a stack then it’s going to be applied in all the environments if you don’t take care of it. You manage environments using tfvars, not tf files.
  • You have to use the terraspace syntax. No way to escape it. Don’t know if is a weak point in truth. You choose terraspace, not the other way around.
  • You need RubyGems to use it.

Repository link:
https://bit.ly/3maXDL0

Follow Me and Subscribe to get the updates on this and the next series!

References:

--

--

Pier
Geek Culture

DevOps Engineer @Microsoft | Working with Python, C++, Node.js, Kubernetes, Terraform, Docker and more