IaC Setup using Terragrunt and Terraform

TA Eng
CodeX
Published in
4 min readFeb 26, 2022

Terragrunt allows you to keep your Terraform code DRY. When I tried it, I wondered how I could leverage it in a similar way where we can keep separate configuration files per environment, and during build/plan and deployment/apply time, pass variables/arguments to Terragrunt CLI.

In this post, I will be describing the setup we are using to push infrastructure changes to dev, stage and production environments. With this setup, we have built CI/CD pipeline to auto-deploy changes to lower environments and manual intervention for production push. We have many smaller repositories, separated by their purpose, for example AWS Organization, Account Bootstrap, IAM Role customization per account/env, Security setup, VPC/Networking setup, Application specific resources, Domain/Microservice resources etc.

Environment

  • Operating System: macOS Monterey
  • Terraenv (Utility to manage multiple version of Terragrunt and Terraform)
  • Terragrunt: v0.36.1
  • Terraform: v1.1.5

Installation Steps

Install Terraenv

brew tap aaratn/terraenv
brew install terraenv

Install Terraform and Terragrunt

#Install
terraenv terraform install 1.1.5
terraenv terragrunt install 0.36.1
#Use installed version
terraenv terraform use 1.1.5
terraenv terragrunt use 0.36.1

Terraform Setup

  • AWS Account. Ideally, AWS SSO Setup with your primary Identity Provider (e.g. Azure Active Directory).
  • Setup AWS Credentials - AWS STS Token or IAM User Access/Secret Key (~/.aws/credentials or as environment variables)
  • S3 bucket and DynamoDB lock table for Terraform State (for multi-region setup, you would want to have separate bucket and DynamoDB table per region)

Validate AWS Credentials Setup

aws sts get-caller-identity{     
"UserId": "...",
"Account": "...",
"Arn": "arn:aws:sts::...:assumed-role/..."
}
OR{
"UserId": "...",
"Account": "...",
"Arn": "arn:aws:iam::...:user/username"
}

Directory Structure

Directory Structure

Terragrunt Config (terragrunt.hcl)

terragrunt.hcl is dependent on two environment variables.

config : Name of Configuration file to use
region : AWS Region to Deploy (config file is looked up in this directory)

Terragrunt setup which looks up config files based on the environment variable set and passes on to Terraform. Also, setting up terraform remote state path by using app, env and region values from config.

Terragrunt Generated Files
Exclude terragrunt generated files (*-generated.tf) in .gitignore

_remote-backend-generated.tf : Contains terraform state config
_provider-generated.tf : Contains terraform provider config
_default-data-generated.tf : Sample data block, to lookup account id, short region code (e.g. us-east-1 => use1)

Note that we are using region,app, env to form a unique path for remote states. {app}/{region}/{env}/terraform.tfstate

Terraform Files

No change to conventional terraform files, main.tf , outputs.tf and variables.tf

variables.tf

#Sample variables.tfvariable "region" { type = string }
variable "app" { type = string }
variable "env" { type = string }
variable "tags" { type = map }
# Other variables
variable "num_servers" { type = number }
variable "bucket_prefix" { type = string }

outputs.tf

#Sample outputs.tf - shows variables from set config file in outputoutput "example" {
value = {
"tags" : var.tags
"region" : local.region
"num_servers" : var.num_servers
"bucket_name" : "${var.app}-${var.bucket_prefix}-${var.env}-${local.region_short}"
}
}

main.tf is empty in our example, as we are just showcasing how variables from different config/environment files can be made available with this setup to terraform.

Configuration Files

config/common.yaml contains configuration common across environments, like Application Name, Tags, Terraform State Bucket, Lock Table and any other config of the resources.

#config/common.yaml#used in terragrunt.hcl
tf_state_bucket: example-state-bucket
tf_state_bucket_region: us-east-1
#app specific common config (app is also used in terragrunt.hcl)
app: example
tags:
Organization: Example-Org

config/us-east-1/dev.yaml Environment specific file, for specific region (in this case, for us-east-1). Values in this config file will be different in one or more environments.

#config/us-east-1/dev.yaml. env is used in terragrunt.hcl as well
env: dev
num_servers: 1
bucket_prefix: mybucket

config/us-east-2/stage.yaml Stage environment’s config file, for us-east-2 region.

#config/us-east-2/stage.yaml
env: stage
num_servers: 3
bucket_prefix: mybucket

config/us-east-2/prod.yaml Stage environment’s config file, for us-east-2 region.

#config/us-east-2/prod.yaml
env: prod
num_servers: 6
bucket_prefix: mybucket

Note bucket_prefix has the same value across all configuration files, so it can be moved to config/common.yaml, other variable values app, region_short and env are composed to create a unique bucket name for each environment.

Build & Deploy

Now that we have terragrunt setup, which uses environment variable config to pick corresponding configuration file, and optionally uses region environment variable, which defaults to us-east-1.

Build (Terraform Plan) for Dev environment

export config=dev
terragrunt init && terragrunt plan

Build (Terraform Plan) for Stage environment

export config=stage
export region=us-east-2 #stage config is in us-east-2 sub-dir
terragrunt init && terragrunt plan

Deploy (Terraform apply) for Dev environment

export config=dev
terragrunt init && terragrunt apply

Deploy (Terraform apply) for Stage environment

export config=stage
export region=us-east-2 #stage config is in us-east-2 sub-dir
terragrunt init && terragrunt apply

As part of your CI/CD pipeline, you can easily come up with pipeline steps which will need to adjust config and region environment variables to get your Infrastructure as Code deployed to selected environment and region. An alternate approach can be terraform workspace, we started with terragrunt originally, and have been happy with our setup so far.

--

--

TA Eng
CodeX
Writer for

Open to Consulting — Software Engineer, Architect, AWS Cloud, Digital Transformation, DevOps, Big Data, Machine Learning