Getting Started with Terragrunt: Simplifying Terraform for Large Projects

MontaF - Sept. 13, 2024

Terragrunt is a thin wrapper for Terraform, designed to make it easier to manage large-scale Terraform configurations. While Terraform is excellent for defining infrastructure as code, managing complex configurations across multiple environments (like dev, staging, and production) can get tricky. This is where Terragrunt comes in—it provides additional tooling to help manage Terraform code, enforce best practices, and handle challenges like DRY (Don’t Repeat Yourself), managing state, and promoting reuse of infrastructure modules.
In this guide, we’ll walk you through Terragrunt, its features, and how it simplifies the use of Terraform, especially in environments with multiple dependencies and configurations. We’ll also set up a simple project with Terragrunt, so you can see how it works in practice.
1. What is Terragrunt?
Terragrunt was created by Gruntwork as an extension to Terraform. Its goal is to:
- DRY Terraform configurations: Avoid repetition by encouraging reusable modules.
- Manage remote Terraform state: Automatically configure and manage the backend (like S3) for Terraform state.
- Manage multi-environment deployments: Simplify handling of different environments (dev, prod, etc.) and keep configurations clean.
- Keep infrastructure code organized: Help with keeping configurations modular and readable, even as projects grow in complexity.
While Terraform is good at defining infrastructure, it doesn’t natively solve some practical problems of managing infrastructure at scale, such as shared configurations or consistent state management. Terragrunt fills these gaps.
2. Installing Terragrunt
You’ll need Terraform installed first since Terragrunt is a wrapper around it. Once you’ve set up Terraform, follow these steps to install Terragrunt.
Installation Steps:
2.1 Install Terragrunt on macOS (via Homebrew):
brew install terragrunt
2.2 Install Terragrunt on Linux/Windows (using the binary):
wget https://github.com/gruntwork-io/terragrunt/releases/download/<VERSION>/terragrunt_linux_amd64
chmod +x terragrunt_linux_amd64
sudo mv terragrunt_linux_amd64 /usr/local/bin/terragrunt
2.3 Verify Installation:
terragrunt --version
This command should output the installed Terragrunt version.
3. Terragrunt vs Terraform: Why Use Terragrunt?
While Terraform is a powerful tool, there are some challenges that developers often face when using it to manage infrastructure across multiple environments.
Here’s how Terragrunt improves the experience:
3.1 Managing Remote State Automatically
In Terraform, you must manually configure the backend (e.g., an S3 bucket) to store the state for each module.
Terragrunt can automate this process and ensure that the remote state is stored in a consistent, secure location.
3.2 DRY Principle and Code Reuse
Terraform can become repetitive when managing multiple environments or modules.
With Terragrunt, you can avoid duplicating code by using the terraform {}
block to reference a reusable module and add environment-specific configuration only once.
3.3 Handling Dependencies
In complex infrastructures, resources often have dependencies on each other.
For instance, a VPC might need to be created before the subnets or EC2 instances.
Terragrunt simplifies handling these dependencies using the dependency
block.
4. Understanding the Basics of Terragrunt
4.1 Folder Structure with Terragrunt
Terragrunt works by organizing Terraform code into modules and leveraging directories to represent environments (like dev
, staging
, prod
). A common structure looks like this:
infrastructure ├── terragrunt.hcl # Root Terragrunt config ├── live │ ├── dev │ │ └── terragrunt.hcl │ └── prod │ └── terragrunt.hcl └── modules └── vpc └── main.tf # Reusable Terraform code for VPC
4.2 The terragrunt.hcl
File
This is where Terragrunt configurations live.
In a Terragrunt project, each environment has its own terragrunt.hcl
file.
Here's a basic example of what a terragrunt.hcl
file looks like:
terraform {
source = "../modules/vpc"
}
inputs = {
region = "us-west-2"
}
In this configuration:
source
: Points to a Terraform module located in../modules/vpc
.inputs
: Provides input variables (like the AWS region) that are passed into the Terraform module.
Terragrunt uses this file to define configurations for each environment (dev
, prod
, etc.).
5. Managing Remote State with Terragrunt
One of the most powerful features of Terragrunt is its automatic state management.
With Terraform alone, you’d need to define the backend configuration for each module manually.
Terragrunt can do this automatically using the remote_state
block.
Here’s how you would configure S3 as the backend for storing Terraform state:
remote_state {
backend = "s3"
config = {
bucket = "my-terraform-state-bucket"
key = "${path_relative_to_include()}/terraform.tfstate"
region = "us-west-2"
}
}
ackend
: Specifies the type of backend (in this case, S3).bucket
: The S3 bucket where the state is stored.key
: The path in the bucket where the state file is saved.region
: The AWS region where the S3 bucket is located.
Using this approach, you don’t need to repeat state configuration across multiple environments—it’s handled centrally by Terragrunt.
6. Setting Up Your First Terragrunt Project
Now that you know the basics, let’s walk through how to set up a simple Terragrunt project that manages AWS infrastructure (such as a VPC).
Step 1: Define the Module
First, we define the reusable Terraform code as a module. Let’s create a modules/vpc/main.tf
file:
provider "aws" {
region = var.region
}
resource "aws_vpc" "main" {
cidr_block = var.cidr_block
tags = {
Name = "MainVPC"
}
}
variable "region" {}
variable "cidr_block" {
default = "10.0.0.0/16"
}
This Terraform code defines a VPC in AWS with a variable cidr_block
. It can be reused for any environment.
Step 2: Configure Terragrunt for Each Environment
Now we’ll set up environment-specific configurations using Terragrunt. For each environment (dev
, prod
), create a terragrunt.hcl
file.
live/dev/terragrunt.hcl
:
terraform {
source = "../../modules/vpc"
}
inputs = {
region = "us-west-2"
cidr_block = "10.1.0.0/16"
}
remote_state {
backend = "s3"
config = {
bucket = "my-terraform-state-bucket"
key = "dev/vpc/terraform.tfstate"
region = "us-west-2"
}
}
live/prod/terragrunt.hcl
:
terraform {
source = "../../modules/vpc"
}
inputs = {
region = "us-west-1"
cidr_block = "10.2.0.0/16"
}
remote_state {
backend = "s3"
config = {
bucket = "my-terraform-state-bucket"
key = "prod/vpc/terraform.tfstate"
region = "us-west-2"
}
}
Now, Terragrunt will automatically:
- Use the
vpc
module for each environment. - Configure the correct
cidr_block
and region for each environment. - Store the Terraform state in the correct S3 path.
Step 3: Initialize and Apply
To deploy the VPC in the dev
environment, navigate to the live/dev
directory and run:
terragrunt init
terragrunt apply
This will initialize and apply the configuration in the dev
environment.
For the prod
environment, navigate to live/prod
and repeat:
terragrunt init
terragrunt apply
Terragrunt manages all the backend state and infrastructure provisioning automatically for each environment.
7. Handling Dependencies in Terragrunt
Terragrunt makes it easy to define dependencies between resources.
For example, if you need to create subnets that depend on the VPC, you can use the dependency
block to ensure the VPC is created first.
Here’s how you can define a dependency on the VPC in the subnets configuration:
dependency "vpc" {
config_path = "../vpc"
outputs = ["vpc_id"]
}
terraform {
source = "../../modules/subnets"
}
inputs = {
vpc_id = dependency.vpc.outputs.vpc_id
}
In this case, the subnets
module depends on the vpc
module, and Terragrunt will ensure that the VPC is created before provisioning the subnets.
Conclusion
Terragrunt is a powerful tool for managing Terraform configurations at scale.
By promoting DRY code, automating remote state management, and simplifying multi-environment setups, Terragrunt significantly enhances the Terraform experience.
In this guide, we covered:
- What Terragrunt is and why it’s useful.
- Installing and setting up Terragrunt.
- Managing Terraform state with Terragrunt.
- Organizing infrastructure using reusable modules.
- Handling dependencies between modules.
By using Terragrunt, you can simplify the management of your Terraform infrastructure, especially in projects with multiple environments and dependencies.
Whether you’re working on AWS, GCP, or another cloud provider, Terragrunt can help you streamline your operations.
Happy coding!