Infrastructure as Code (IaC) is a critical component in modern cloud deployments, and Terraform has become one of the most popular tools for defining cloud resources. But when you manage complex infrastructure spread across multiple environments, the problem of duplicate code often arises. That’s where Terragrunt comes into play.
In this article, I’ll explain how Terragrunt can help simplify your Terraform workflow, avoid redundancy, and enable reusable cloud infrastructure by deploying resources in multiple modules.
Let's dive in! 🏗️
What is Terraform?
Terraform is an open-source tool for building, changing, and versioning infrastructure safely and efficiently. It allows users to define cloud resources declaratively using HCL (HashiCorp Configuration Language) and automates the provisioning process across cloud providers like AWS, GCP, and Azure.
While Terraform is extremely powerful, managing different environments like dev
, staging
, and production
can lead to repetitive code as you define similar resources across multiple modules or environments. This is where Terragrunt steps in.
What is Terragrunt?
Terragrunt is a thin wrapper around Terraform that provides extra tools for working with multiple Terraform modules. It simplifies the process of deploying infrastructure by handling things like remote state management, managing dependencies, and reducing duplicate code by using reusable configurations.
In simple terms, Terragrunt helps you DRY (Don't Repeat Yourself) your Terraform code.
Why Use Terragrunt with Terraform?
As your infrastructure grows, you often find yourself writing the same Terraform code over and over for each environment or module. For example, you might define an S3 bucket in dev
, staging
, and prod
environments. This leads to code duplication, which can become hard to maintain.
Here’s why you should consider Terragrunt:
- DRY Principle: Eliminate duplicate definitions by defining reusable Terraform configurations.
- Simplified Infrastructure: Keep your environment-specific configurations separate from your infrastructure definitions.
- Easy Multi-Environment Support: Deploy the same infrastructure across multiple environments with ease.
- Remote State Management: Automatically configure remote state and locking.
Applying Terraform Code with Terragrunt 🛠️
Let’s go through an example where we use Terragrunt to reduce duplicate Terraform code by deploying cloud resources across multiple environments.
Step 1: Organize Your Terraform Modules
First, you need to structure your Terraform code into reusable modules. For example, let’s say you want to create an S3 bucket and an RDS database for your application. Here’s how you can structure the Terraform code:
└── terraform-modules/
├── s3/
│ └── main.tf
├── rds/
│ └── main.tf
Inside each module (s3/main.tf and rds/main.tf), you define the infrastructure resources, for example:
# s3/main.tf
resource "aws_s3_bucket" "this" {
bucket = var.bucket_name
}
# rds/main.tf
resource "aws_db_instance" "this" {
identifier = var.db_name
engine = "mysql"
instance_class = "db.t3.micro"
}
Step 2: Write Terragrunt Configuration
Now that you have your reusable Terraform modules, you can use Terragrunt to apply these modules across multiple environments like dev, staging, and production.
Create the following directory structure:
└── live/
├── dev/
│ └── terragrunt.hcl
├── staging/
│ └── terragrunt.hcl
├── prod/
│ └── terragrunt.hcl
In each terragrunt.hcl file, you reference the Terraform module and define any environment-specific variables. For example, here’s the configuration for the dev environment:
# live/dev/terragrunt.hcl
terraform {
source = "../../terraform-modules/s3"
}
inputs = {
bucket_name = "my-app-dev-bucket"
}
For the staging environment:
# live/staging/terragrunt.hcl
terraform {
source = "../../terraform-modules/s3"
}
inputs = {
bucket_name = "my-app-staging-bucket"
}
As you can see, you reuse the same Terraform module and only change the inputs (like the bucket_name) for each environment. Terragrunt automatically ensures that the module is applied correctly for each environment.
Step 3: Manage Remote State
Terragrunt makes it easy to manage remote state for Terraform. You can define a common backend configuration in a root terragrunt.hcl file, which all environments inherit:
# live/terragrunt.hcl
remote_state {
backend = "s3"
config = {
bucket = "my-terraform-state-bucket"
key = "${path_relative_to_include()}/terraform.tfstate"
region = "us-east-1"
dynamodb_table = "terraform-locks"
}
}
Now, all environments (dev, staging, prod) will use this same S3 bucket to store their Terraform state, making it easy to manage and share the state between different environments.
The Benefits of Using Terragrunt
By applying Terragrunt in your Terraform workflow, you unlock several advantages:
Reduced Duplication: Instead of copying and pasting similar Terraform code across environments, you create reusable modules and simply adjust inputs for each environment.
Easy Environment Management: Spin up new environments with just a few changes in the terragrunt.hcl files.
Centralized State Management: With Terragrunt, managing remote state is as simple as defining it once in the root configuration.
Clear Separation of Concerns: Separate your infrastructure logic (modules) from environment-specific configurations (Terragrunt configs).
Final Thoughts
By using Terragrunt with Terraform, you can significantly reduce the complexity and repetition in your infrastructure code. This approach scales well as your infrastructure grows, making it easier to manage different environments and resources.
If you’re working with multi-environment setups or struggling with redundant Terraform code, Terragrunt is an excellent tool to streamline your workflow.
Feel free to leave any questions in the comments or share your experiences using Terraform and Terragrunt. Happy coding! 🎉
Related Resources:
Terragrunt Documentation
Gruntwork's Guide to Terragrunt
Top comments (0)