angacom expo

17-19 June

Bella Center, Copenhagen, Denmark

DTW Ignite 2025

Let's meet!
CEO Volodymyr Shynkar
HomeBlogTerragrunt Tutorial for DevOps
Best PracticesKey DifferencesTechnologiesTerragrunt

Terragrunt Tutorial for DevOps

8 mins
29.04.2025
Volodymyr Shynkar CEO and Co-Founder of AppRecode

Volodymyr Shynkar

CEO/CTO

What Is Terragrunt?

Terragrunt acts as a wrapper for Terraform to help support DRY (Don’t Repeat Yourself) configuration, and to automate certain tasks. It was built by Gruntwork, and helps to cover many emerging operational pain points when scaling up your Terraform use.

The tool doesn’t replace Terraform – it enhances it. Think of Terragrunt as a helpful assistant that handles repetitive tasks, manages remote state, and provides improved module management capabilities.

Why consider using Terragrunt? If you’ve ever:

  • Found yourself copying and pasting similar Terraform code across environments
  • Struggled with remote state management
  • Needed better ways to handle variables across multiple environments
  • Wanted to execute Terraform commands on multiple modules at once

Then Terragrunt might be exactly what you’re looking for. Many teams implementing devops development and consulting services find Terragrunt invaluable for managing complex infrastructure at scale.

How Terragrunt Works: Core Concepts

To understand how Terragrunt works, familiarize yourself with these core concepts:

terragrunt.hcl Files

The heart of Terragrunt is the terragrunt.hcl configuration file. This file contains Terragrunt-specific configuration and can inherit from parent directories, enabling better code organization with hierarchical configurations.

Remote State Management

Terragrunt simplifies remote state configuration by providing a single place to define it. Instead of copying the same backend configuration across every module, you can define it once in a parent terragrunt.hcl file.

Input Variables

Terragrunt lets you define and manage input variables centrally. You can set variables at different levels (root, environment, component) and have them automatically passed to Terraform.

Dependencies

Terragrunt elegantly handles dependencies between modules. You can explicitly define dependencies, allowing Terragrunt to determine the correct order for applying or destroying resources.

Keep Your Code DRY

One of Terragrunt’s main purposes is eliminating repetition. By enabling configuration inheritance, you avoid duplicating common settings across multiple Terraform modules.

Terragrunt Modules & Architecture Best Practices

When working with Terragrunt, following thoughtful architecture patterns helps maximize its benefits:

Hierarchical Directory Structure

A well-organized directory structure might look like:

├── terragrunt.hcl                 # Root configuration

├── prod

│   ├── terragrunt.hcl             # Production environment configuration

│   ├── vpc

│   │   └── terragrunt.hcl         # VPC configuration for production

│   └── databases

│       └── terragrunt.hcl         # Database configuration for production

└── dev

    ├── terragrunt.hcl             # Development environment configuration

    ├── vpc

    │   └── terragrunt.hcl         # VPC configuration for development  

    └── databases

        └── terragrunt.hcl         # Database configuration for development

Configuration Inheritance

Terragrunt’s inheritance model means child configurations automatically receive settings from parent configurations. This works beautifully for environment-specific settings.

Centralized Module Source

Use a centralized repository for Terraform modules and reference them in Terragrunt configurations. This approach significantly reduces duplication and enforces standardization.

Environment-Specific Variables

Store environment-specific variables in the appropriate terragrunt.hcl files. This keeps your infrastructure definitions consistent while allowing for environment-specific values.

When structuring your configuration, always remember that organizations implementing managed cloud services benefit from thoughtful architecture that reduces maintenance complexity.

Essential Terragrunt Commands You Need

These commands form the foundation of working with Terragrunt:

terragrunt init: initializes the working directory by downloading the necessary providers and modules.

terragrunt plan: shows what changes Terragrunt will make to your infrastructure.

terragrunt apply: applies the changes required to reach the desired state.

terragrunt destroy: destroys all resources managed by the current configuration.

terragrunt run-all: runs a command across multiple Terragrunt modules.

terragrunt output: extracts the outputs from the Terraform state.

These commands closely mirror their Terraform counterparts, making the transition to Terragrunt straightforward for teams familiar with Terraform.

Step-by-Step Terragrunt Tutorial

Let's walk through setting up a basic Terragrunt project:

Step 1: Install Terragrunt

For macOS users with Homebrew:

brew install terragrunt

For Linux users:

curl -Lo terragrunt https://github.com/gruntwork-io/terragrunt/releases/download/v0.45.0/terragrunt_linux_amd64

chmod +x terragrunt

sudo mv terragrunt /usr/local/bin/

Step 2: Create Your Project Structure

Create a directory structure for a simple project:

mkdir -p terragrunt-demo/{terragrunt.hcl,dev/{vpc,rds}/terragrunt.hcl,prod/{vpc,rds}/terragrunt.hcl}

Step 3: Define Root Configuration

In the root terragrunt.hcl, set up remote state configuration:

# terragrunt-demo/terragrunt.hcl

remote_state {

  backend = “s3”

  config = {

    bucket         = “my-terraform-state”

    key            = “${path_relative_to_include()}/terraform.tfstate”

    region         = “us-west-2”

    encrypt        = true

    dynamodb_table = “terraform-locks”

  }

}

 

# Generate provider configuration for all child configurations

generate “provider” {

  path      = “provider.tf”

  if_exists = “overwrite_terragrunt”

  contents  = <<EOF

provider “aws” {

  region = “us-west-2”

}

EOF

}

Step 4: Define Environment-Specific Configurations

For the development environment:

# terragrunt-demo/dev/terragrunt.hcl

include {

  path = find_in_parent_folders()

}

 

inputs = {

  environment = “dev”

  instance_type = “t3.small”

}

 

For production:

# terragrunt-demo/prod/terragrunt.hcl

include {

  path = find_in_parent_folders()

}

 

inputs = {

  environment = “prod”

  instance_type = “m5.large”

}

Step 5: Define Module Configurations

For the VPC in development:

# terragrunt-demo/dev/vpc/terragrunt.hcl

include {

  path = find_in_parent_folders()

}

 

terraform {

  source = “tfr://registry.terraform.io/terraform-aws-modules/vpc/aws?version=3.14.0”

}

 

inputs = {

  name = “dev-vpc”

  cidr = “10.0.0.0/16”

  

  azs             = [“us-west-2a”, “us-west-2b”]

  private_subnets = [“10.0.1.0/24”, “10.0.2.0/24”]

  public_subnets  = [“10.0.101.0/24”, “10.0.102.0/24”]

  

  enable_nat_gateway = true

  single_nat_gateway = true

}

 

It gives a great deal of flexibility in your code while maintaining consistent environments.

Terragrunt vs Terraform: Key Differences

It’s important to understand when looking at Terragrunt and Terraform what they do and how they are related:

Terraform: The core IaC tool that defines and provisions infrastructure.

Terragrunt: A thin wrapper around Terraform that provides:

  • Better code reuse through inheritance
  • Simplified remote state management
  • Dependency management between components
  • Multi-module workflows

Terraform works perfectly for simple projects, but as complexity grows, Terragrunt adds valuable capabilities that help manage that complexity. Organizations implementing cloud migration solutions often find Terragrunt essential for maintaining sanity during large migrations.

Real Terragrunt Example

Let’s examine a real-world example where Terragrunt shines. Imagine managing a multi-environment AWS infrastructure with shared components:

Root terragrunt.hcl

remote_state {

  backend = “s3”

  config = {

    bucket         = “acme-terraform-states”

    key            = “${path_relative_to_include()}/terraform.tfstate”

    region         = “us-east-1”

    encrypt        = true

    dynamodb_table = “terraform-locks”

  }

}

 

Pass common variables to all child Terragrunt configurations

inputs = {

  company_name = “ACME”

  aws_region   = “us-east-1”

}

 

Common AWS provider for all modules

generate “provider” {

  path      = “provider.tf”

  if_exists = “overwrite_terragrunt”

  contents  = <<EOF

provider “aws” {

  region = “${local.aws_region}”

  

  default_tags {

    tags = {

      Environment = “${local.environment}”

      Terraform   = “true”

      Project     = “${local.project}”

    }

  }

}

EOF

}

 

Environment-specific configuration:

environments/production/terragrunt.hcl

include {

  path = find_in_parent_folders()

}

 

locals {

  environment = “production”

  project     = “main-app”

}

 

inputs = {

  environment      = local.environment

  instance_types   = [“m5.large”]

  high_availability = true

  backup_retention = 30

}

 

Component-specific configuration:

environments/production/database/terragrunt.hcl

include {

  path = find_in_parent_folders()

}

 

dependency “vpc” {

  config_path = “../vpc”

  

  Configure mock outputs for the vpc module to allow

  database plan to work even when VPC isn’t created yet

  mock_outputs = {

    private_subnets = [“subnet-123”, “subnet-456”]

    vpc_id = “vpc-123”

  }

}

 

terraform {

  source = “github.com/terraform-aws-modules/terraform-aws-rds?ref=v3.4.0”

}

 

inputs = {

  identifier           = “production-db”

  

  Get values from the VPC module output

  subnet_ids           = dependency.vpc.outputs.private_subnets

  vpc_security_group_ids = [dependency.vpc.outputs.database_security_group_id]

  

  engine               = “postgres”

  engine_version       = “13.4”

  instance_class       = “db.m5.large”

  allocated_storage    = 100

  

  name                 = “appdb”

  username             = “dbadmin”

  password             = “get_from_parameter_store”

  

  backup_retention_period = 30

  

  Additional RDS parameters…

}

 

This example demonstrates several Terragrunt strengths:

  • Consistent remote state configuration
  • Environment-specific variables
  • Dependencies between components
  • Code reuse through centralized modules

Organizations concerned with managed cloud security services appreciate how Terragrunt helps maintain security standards across environments through configuration inheritance.

Conclusion

Terragrunt solves many of Terraform’s limitations when working with more complex multi-environment infrastructure projects. Terragrunt’s ability to keep configurations DRY, automate common workflows, and manage dependencies makes the infrastructure-as-code experience substantially better. 

There is a learning curve to Terragrunt, but the investment in learning pays dividends quickly for teams which manage infrastructure at scale. The modularity and inheritance features of Terragrunt give teams the ability to standardize their infrastructure while still accommodating the changes necessary for different environments. 

As cloud infrastructure continues to become more and more complicated, tools like Terragrunt become increasingly more important for keeping things manageable and consistent. If you are suffering from Terraform sprawl and copy/pasting configurations, Terragrunt brings tremendous value and should be seriously considered.

Frequently asked questions

Do I need to learn Terraform before using Terragrunt?

Yes, absolutely. Terragrunt is a wrapper around Terraform, not a replacement. You’ll need solid Terraform knowledge since Terragrunt simply enhances Terraform’s capabilities but relies on the same underlying concepts and module structure.

Is Terragrunt suitable for large infrastructure projects?

Terragrunt excels in large, complex infrastructure projects. In fact, its benefits become more apparent as projects scale up. The ability to maintain DRY configurations and manage dependencies becomes increasingly valuable with project size.

Can Terragrunt be used with CI/CD pipelines?

Yes, Terragrunt works well in CI/CD pipelines. Its run-all command is particularly useful for automation. Many teams integrate Terragrunt into their deployment pipelines to automatically apply infrastructure changes after appropriate testing and approval.

What is a terragrunt.hcl file and why is it important?

A terragrunt.hcl file contains Terragrunt-specific configuration. It’s crucial because it defines how Terragrunt should interact with Terraform, including remote state configuration, dependencies, inputs, and hooks. The hierarchical nature of these files enables configuration inheritance.

Can I use Terragrunt with AWS and Azure at the same time?

Yes, Terragrunt can work with multiple cloud providers simultaneously. You can organize your directory structure to include different cloud providers and use the appropriate provider configuration in each set of modules. This makes Terragrunt excellent for multi-cloud strategies.

Did you like the article?

0 ratings, average 0 out of 5

Comments

Loading...

Blog

OUR SERVICES

REQUEST A SERVICE

651 N Broad St, STE 205, Middletown, Delaware, 19709
Ukraine, Lviv, Studynskoho 14

Get in touch

Contact us today to find out how DevOps consulting and development services can improve your business tomorrow.