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.
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;
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:
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.