Skip to content

DRY Configuration with include

The include block is how a unit’s terragrunt.hcl pulls in everything defined in root.hcl — or in any intermediate config file — without retyping any of it.

Last lesson’s vpc/terragrunt.hcl opened with this block, and it deserves a proper explanation now:

prod/us-east-1/vpc/terragrunt.hcl
include "root" {
path = find_in_parent_folders("root.hcl")
}

find_in_parent_folders("root.hcl") walks up the directory tree from wherever the current unit lives, one directory at a time, until it finds a file literally named root.hcl. include "root" then merges everything that file defines into the current unit’s configuration, as though it had been typed there directly — the remote_state block, the generated backend.tf, the provider generate block, all of it.

Why the search matters: it means the exact same include line works whether a unit sits one directory below root.hcl or five directories deep. Move a unit into a new subdirectory for a reorganization, and this line keeps working unchanged — it never hard-codes a relative path like ../../../root.hcl that would break the moment the tree is reshaped.

locals and read_terragrunt_config for one level down

Section titled “locals and read_terragrunt_config for one level down”

include is not the only inheritance mechanism, and it is not always the right one — sometimes you want to share a smaller, more specific value with only part of the tree, not the whole thing. Picture an intermediate region.hcl file sitting one level below the true root, covering just the units that belong to a specific AWS region:

prod/us-east-1/region.hcl
locals {
aws_region = "us-east-1"
}

A unit under that region’s directory reads it with read_terragrunt_config(), combined with the same find_in_parent_folders() you already know:

prod/us-east-1/vpc/terragrunt.hcl
include "root" {
path = find_in_parent_folders("root.hcl")
}
locals {
region_vars = read_terragrunt_config(find_in_parent_folders("region.hcl"))
}
terraform {
source = "git::[email protected]:acme/infrastructure-modules.git//vpc?ref=v1.4.0"
}
inputs = {
aws_region = local.region_vars.locals.aws_region
cidr_block = "10.0.0.0/16"
}

read_terragrunt_config() parses another Terragrunt config file and hands back its contents as a value, and that file’s own locals block ends up nested under .locals on the result — hence local.region_vars.locals.aws_region. A unit under prod/ap-southeast-1/ would have its own region.hcl defining aws_region = "ap-southeast-1", while every unit anywhere in the tree still shares the same root.hcl for its backend and provider. Same root, different region per subtree, without a single unit hard-coding which region it belongs to inline.

inputs: the Terragrunt equivalent of a tfvars file

Section titled “inputs: the Terragrunt equivalent of a tfvars file”

Every example so far has ended with an inputs = { ... } block, and it deserves its own explanation. This is how a unit supplies its underlying Terraform module’s input variables — functionally the same job a .tfvars file does for a plain Terraform root module, except expressed directly as Terragrunt HCL rather than as a separate file Terraform has to be told to load. Each key in inputs maps to a variable the module declares, and Terragrunt passes the whole block through to Terraform as -var values under the hood.

Because inputs is regular HCL rather than a static .tfvars file, it can reference anything else in the same config — local.region_vars.locals.aws_region above is one example, and the next lesson introduces referencing another unit’s outputs the same way.

flowchart TD
  root["root.hcl (remote_state + generate)"]
  region["prod/us-east-1/region.hcl (locals.aws_region)"]
  unit["prod/us-east-1/vpc/terragrunt.hcl"]
  root -->|include + find_in_parent_folders| unit
  region -->|read_terragrunt_config + find_in_parent_folders| unit
  unit --> inputs["inputs = aws_region, cidr_block"]
root.hcl and region.hcl combining into one unit's config
What does the include block with find_in_parent_folders actually do?
What is read_terragrunt_config used for with an intermediate file like region.hcl?
The inputs block in a unit terragrunt.hcl is the Terragrunt equivalent of what?
Why does find_in_parent_folders keep working after a unit is moved deeper into a new subdirectory?