2025-12-25
Getting Started with Terragrunt

Getting Started with Terragrunt
I hope you've heard about the Infrastructure as Code (IaC) tool Terraform. Recently, I had the opportunity to work with another powerful tool that complements Terraform: Terragrunt.
Terragrunt is a thin wrapper around Terraform that helps manage Terraform configurations more efficiently, especially when working with:
- Multiple environments (Development, Testing, Staging, Production)
- Multiple cloud accounts
- Multiple regions
Its primary goal is to eliminate code duplication while simplifying configuration management and remote state handling.
Why Terragrunt Exists
As Terraform projects grow, several common challenges appear:
- Repeating the same backend configuration in multiple folders
- Copy-pasting variables across environments
- Managing remote state manually
- Handling dependencies between Terraform modules
Terragrunt addresses these problems by providing a cleaner and more maintainable project structure.
What Terragrunt Does
1. DRY (Don't Repeat Yourself)
Instead of writing the same Terraform configuration repeatedly:
- Create Terraform modules once
- Reuse them across multiple environments using Terragrunt
2. Remote State Management
Terragrunt automatically configures remote backends such as:
- Azure Storage
- AWS S3
- DynamoDB (State Locking)
This removes the need to define backend blocks inside every Terraform module.
3. Environment Separation
Maintain a clean directory structure for environments such as:
- Development
- Staging
- Production
Each environment can reuse the same Terraform modules while supplying different inputs.
4. Dependency Management
Terragrunt allows modules to depend on one another by:
- Defining module dependencies
- Passing outputs between modules safely
5. Infrastructure Orchestration
Execute operations across multiple modules with a single command.
Examples:
terragrunt applyterragrunt destroy
This is especially useful for large infrastructure deployments.
Prerequisites
Before getting started, install the following:
- Azure CLI (if using Azure)
- Terraform Version Manager (tfenv)
- Terragrunt Version Manager (tgenv)
Additionally, create the required remote state resources beforehand.
For Azure:
- Resource Group
- Storage Account
For AWS:
- S3 Bucket
- DynamoDB Table (for state locking)
Step 1 - Create the Root terragrunt.hcl
locals {
default_yaml_path = find_in_parent_folders("empty.yaml")
org = yamldecode(file(find_in_parent_folders("org.yaml")))
subscription = yamldecode(file(find_in_parent_folders("subscription.yaml")))
resource_group = yamldecode(file(find_in_parent_folders("resource_group.yaml")))
environment = yamldecode(file(find_in_parent_folders("environment.yaml", local.default_yaml_path)))
}
generate "provider" {
path = "provider.tf"
if_exists = "overwrite_terragrunt"
contents = <<EOF
provider "azurerm" {
features {}
subscription_id = "${local.subscription.subscription_id}"
}
EOF
}
remote_state {
backend = "azurerm"
generate {
path = "backend.tf"
if_exists = "overwrite"
}
config = {
subscription_id = local.subscription.state_subscription
resource_group_name = local.subscription.state_resource_group
storage_account_name = local.subscription.state_storage_account
container_name = "terraform-state-v2"
key = "${path_relative_to_include()}/terraform.tfstate"
}
}
inputs = merge(
local.org,
local.subscription,
local.resource_group,
local.environment
)
Step 2 - Create org.yaml
org_name: your_org_name_azure
Step 3 - Create empty.yaml
{}
Step 4 - Directory Structure
Create a directory for your subscription, followed by resource groups and infrastructure resources.
subscriptions/
└── Tharinduksubscription/
├── resource-groups/
│ └── terra-rg/
│ ├── buckets/
│ │ └── terra-bucket/
│ ├── resourcegroup/
│ ├── resource_group.yaml
│ ├── subscription.yaml
│ ├── .gitignore
│ ├── README.md
│ ├── empty.yaml
│ ├── org.yaml
│ └── terragrunt.hcl
Step 5 - Configure subscription.yaml
This file contains the configuration required for remote state management.
state_subscription:
state_resource_group: terraform-state
state_storage_account:
subscription_id:
Step 6 - Configure resource_group.yaml
location: uksouth
resource_group_name: terra-rg
Step 7 - Create Resource Modules
Each infrastructure resource should have its own directory.
Example structure:
resourcegroup/
├── terragrunt.hcl
├── variables.tf
└── resourcegroup.tf
Resource Definition
resource "azurerm_resource_group" "resource_group" {
location = var.location
name = var.resource_group_name
}
terragrunt.hcl
include {
path = find_in_parent_folders()
}
variables.tf
variable "location" {}
variable "resource_group_name" {}
Step 8 - Deploy Infrastructure
Once everything is configured, execute:
terragrunt plan
Then deploy:
terragrunt apply
Terragrunt will automatically:
- Configure the backend
- Generate provider configuration
- Read shared variables
- Execute Terraform
Conclusion
Terragrunt significantly simplifies managing Terraform infrastructure by eliminating repetitive configuration, organizing environments, and automating remote state management.
Although the initial setup requires some patience, it greatly improves maintainability as infrastructure grows. Once configured, managing multiple environments becomes much cleaner and more scalable.
Key Benefits
- Less code duplication
- Easier multi-environment management
- Automatic remote state configuration
- Cleaner project structure
- Built-in dependency management
- Better scalability for large infrastructure projects
