Articles - Merapar

Keeping your Terraform DRY using Terraform

Written by Leon Lanen | Jul 3, 2023 12:36:23 PM

In a series of blog posts we will focus on some of the best practices we use within Merapar to evolve our DevOps practices we have built around the GCP platform. Why? Because we think it’s fun to share knowledge and to learn from others in the industry.

Situation

Terraform (https://www.Terraform.io/)  is a great tool used to declare and deploy your Infrastructure as Code, Terraform is our preferred tool when doing GCP projects. We do have a challenge in each project on how to re-use the Terraform definitions across multiple environments. 

When doing so;

  • We like to keep the state completely separated for each environment, this allows us to, at least, have separate credentials for different staging environments.
  • We want to be as declarative as possible when defining variables for different staging environments.

 

Solution

To solve this, we often use Terragrunt (https://terragrunt.gruntwork.io/), terragrunt is a tiny wrapper for Terraform which promises to keep your code Don’t Repeat Yourself (DRY). And for sure, it does so for us!

When using terragrunt we use the following directory/file structure:

From within the development or production directory we can call “terragrunt apply”, this will:

  • process the current terragrunt file
  • include the top level terragrunt file
  • finally include the infrastructure directory by which we deploy the actual infrastructure.

As terragrunt is a tiny wrapper, it will pre-process the files, and call Terraform to perform the actual deployments. The terragrunt binary can be used, with a few exceptions, as you would normally call the Terraform binary.

Let’s break this down into some examples:

/<environment>/terragrunt.hcl

include
With this definition, we first of all say that we want to include the parent folder. This contains another terragrunt file with further instructions on what to do.

inputs
Within the inputs section, we define the value for any environment specific variables we need.

/terragrunt.hcl

This terragrunt definition file has been included from the environment specific file and is valid for each environment. 

Terraform
First of all, we are including the Terraform sources which are required to be deployed. Note the double // in "source = ${get_parent_terragrunt_dir()}//infra", it is required to indicate to terragrunt from where to include files in a Terraform working directory

remote_state
We define the remote backend in this file, and template the name of the bucket where we want to store the state with the “path_relative_to_include()”, in this setup it will populate the directory name where we included it from. So either “development” or “production”.

inputs
We define any variables which are valid for each environment, possibly filled in with terragrunt specific functions.

/infrastructure/variables.tf

As normally within Terraform, we define the variables. The variables are populated by Terragrunt using input blocks. 

In our resource definitions, we will never use any environment logic. Variables are injected in a clean way by which it is easy to spin-up or remove an environment.