Terragrunt: keeping it DRY
Wrap Terraform for many environments.
As the directory-per-environment pattern grows, you notice new duplication: every environment’s config repeats the same backend block, the same provider setup, the same module calls with slightly different inputs. Terragrunt is a thin wrapper around Terraform whose whole purpose is to keep that DRY (Don’t Repeat Yourself). It lets you define the backend and common configuration once and inherit it everywhere, and call modules with per-environment inputs from small terragrunt.hcl files. It does not replace Terraform — it orchestrates it.
# root terragrunt.hcl — define the backend ONCE for all environmentsremote_state {backend = "s3"config = {bucket = "acme-tfstate"key = "${path_relative_to_include()}/terraform.tfstate" # auto per-dir keyencrypt = truedynamodb_table = "tf-locks"}}# environments/prod/network/terragrunt.hcl — tiny: include root + call the moduleinclude "root" { path = find_in_parent_folders() }terraform { source = "git::github.com/acme/modules.git//network?ref=v1.4.0" }inputs = { cidr = "10.0.0.0/16", env = "prod" }
What Terragrunt removes
The repetition Terragrunt eliminates is real: the backend configuration (defined once, with the state key derived automatically from each directory’s path), the provider generation, and the boilerplate around calling modules. Each environment/component becomes a tiny terragrunt.hcl that includes the shared root and supplies only its inputs. It also adds conveniences like running Terraform across many modules with dependencies in the right order (run-all), which helps when a large estate is split into many small state files.
When to reach for it
Terragrunt earns its place when you have many environments and/or many small components (the split-state pattern), where the backend/boilerplate duplication and cross-module orchestration become painful. For a small project with one or two environments, plain Terraform with a directory per environment is simpler and Terragrunt is overhead. It is a "scale" tool: adopt it when the repetition it removes is actually hurting, not preemptively. Note some of its features (like generated backends) have since appeared in Terraform itself, so evaluate what you actually need.