
Terragrunt is a powerful tool that simplifies and enhances Terraform usage by providing better code reusability, DRY (Don’t Repeat Yourself) configurations, and easier multi-environment deployments. In this guide, we’ll explore how to install, configure, and leverage Terragrunt for complex infrastructure deployments.
In this guide, we’ll walk through:
- Installing and configuring Terragrunt
- Creating DRY Terraform configurations using Terragrunt
- Advanced examples including dependency management, multiple environments, and hooks
- FAQs with examples to help you master Terragrunt and prepare for technical interviews
Prerequisites
Before getting started, ensure you have:
- Terraform installed — https://developer.hashicorp.com/terraform/install
- Terragrunt installed -https://terragrunt.gruntwork.io/docs/getting-started/install/
- A basic understanding of Terraform and its modules
Step 1: Installing Terragrunt
macOS (using Homebrew)
brew install terragrunt
Linux
Download the latest release from the Terragrunt GitHub Releases page, then extract and move it to your PATH:
wget https://github.com/gruntwork-io/terragrunt/releases/download/v0.47.0/terragrunt_linux_amd64
chmod +x terragrunt_linux_amd64
sudo mv terragrunt_linux_amd64 /usr/local/bin/terragrunt
Verify your installation:
terragrunt --version
Step 2: Organizing Your Terraform Code with Terragrunt
Terragrunt helps you avoid repetition by maintaining a single DRY configuration file that is included across multiple Terraform modules. Consider the following folder structure for multiple environments:
infrastructure/
├── live
│ ├── dev
│ │ ├── network
│ │ │ └── terragrunt.hcl
│ │ └── compute
│ │ └── terragrunt.hcl
│ └── prod
│ ├── network
│ │ └── terragrunt.hcl
│ └── compute
│ └── terragrunt.hcl
└── modules
├── network
│ └── main.tf
└── compute
└── main.tf
Root Terragrunt Configuration
Create a root configuration (e.g., infrastructure/live/terragrunt.hcl) to set common settings for all environments:
# Root terragrunt.hcl
remote_state {
backend = "s3"
config = {
bucket = "my-terraform-state-bucket"
key = "${path_relative_to_include()}/terraform.tfstate"
region = "us-west-2"
encrypt = true
dynamodb_table = "terraform-locks"
}
}
locals {
common_tags = {
Project = "TerragruntDemo"
}
}
Each child module (e.g., network/terragrunt.hcl) includes the root configuration:
# infrastructure/live/dev/network/terragrunt.hcl
include {
path = find_in_parent_folders()
}
terraform {
source = "../../../modules/network"
}
inputs = {
environment = "dev"
tags = local.common_tags
}
Step 3: Advanced Terragrunt Examples
1. Managing Dependencies
Terragrunt can automatically pass outputs from one module as inputs to another using the dependency block.
# infrastructure/live/dev/compute/terragrunt.hcl
include {
path = find_in_parent_folders()
}
dependency "network" {
config_path = "../network"
}
terraform {
source = "../../../modules/compute"
}
inputs = {
environment = "dev"
vpc_id = dependency.network.outputs.vpc_id
tags = local.common_tags
}
2. Using Hooks for Pre/Post-Processing
terraform {
source = "../../../modules/compute"
}
before_hook "validate" {
commands = ["apply", "plan"]
execute = ["bash", "${get_terragrunt_dir()}/../../scripts/validate.sh"]
}
3. Advanced Remote State Configuration
# infrastructure/live/prod/network/terragrunt.hcl
include {
path = find_in_parent_folders()
}
terraform {
source = "../../../modules/network"
}
inputs = {
environment = "prod"
tags = local.common_tags
}
remote_state {
config = merge(
local.remote_state_config,
{ key = "prod/network/terraform.tfstate" }
)
}
Step 4: Running Terragrunt Commands
terragrunt init
terragrunt plan
terragrunt apply
terragrunt destroy
FAQs:
Q1: What is Terragrunt and why should I use it?
A: It helps keep Terraform configurations DRY (Don’t Repeat Yourself), manage remote state, and handle dependencies.
Q2: How does Terragrunt dependency management work?
A: Using the dependency block, Terragrunt ensures that one module’s outputs are available to another.
Example:
dependency "network" {
config_path = "../network"
}
inputs = {
vpc_id = dependency.network.outputs.vpc_id
}
Q3: How do hooks work in Terragrunt?
A: Hooks let you run custom scripts before or after Terraform commands.
Example:
before_hook "validate" {
commands = ["apply", "plan"]
execute = ["bash", "${get_terragrunt_dir()}/../../scripts/validate.sh"]
}
Q4: How does Terragrunt simplify remote state management?
A: It centralizes state configuration in a root terragrunt.hcl file, avoiding repetition.
Q5: How can I troubleshoot issues with Terragrunt?
A: Run Terraform commands with increased logging:
export TF_LOG=DEBUG
terragrunt plan
Conclusion
Terragrunt elevates your Terraform workflow by reducing duplication, managing remote state effortlessly, and handling module dependencies with ease.
Key Takeaways:
✅ Install and set up Terragrunt
✅ Organize infrastructure code for multiple environments
✅ Use dependency management and hooks
✅ Address common questions with examples
If you found this guide helpful, follow me for more DevOps and Infrastructure as Code tutorials! 🚀
📢 Have questions or feedback? Drop a comment below or connect with me on Twitter/X@spysood!
Originally published on Medium.