Why Terragrunt
The idea in one sentence
Section titled “The idea in one sentence”Terraform workspaces solve DRY reuse but leave isolation weak, directory-per-environment solves isolation but reintroduces duplicated backend and provider boilerplate, and Terragrunt exists to close that remaining gap by wrapping the real terraform binary and generating the repeated configuration for you.
The exact tension, restated precisely
Section titled “The exact tension, restated precisely”The last two lessons each solved half of the problem from the very first lesson in this module:
- Workspaces keep one copy of the
.tfcode, one backend, and one provider configuration — genuinely DRY — but every workspace shares that same backend and the same GCPproject, so isolation between environments is weak enough that a wrongterraform workspace selectcan apply prod-sized changes against what someone assumed was a disposable environment. - Directory-per-environment gives every environment its own backend and its own GCP project — genuinely isolated — but the backend block, the provider block, and the module
sourceand version wiring end up copy-pasted nearly verbatim into every environment directory, which is the exact same manual-repetition risk the first lesson in this module opened with.
Plain Terraform, on its own, does not have a single primitive that gives you both strong isolation and zero configuration duplication across environments at the same time. You can pick one of the two tensions above, but not escape both with .tf code alone.
What Terragrunt actually is
Section titled “What Terragrunt actually is”Terragrunt is a thin wrapper and orchestrator that sits on top of the real terraform (or tofu) binary. It does not introduce a new resource syntax, and it does not compile to some different infrastructure-as-code language — the .tf module code you already write, including everything in the modules/app directory from the previous lesson, stays completely unchanged and is still plain Terraform. Every single Terragrunt command ultimately shells out to a real terraform (or tofu) command underneath:
# What you typeterragrunt apply
# What actually eventually runs, per unit, with generated config already in placeterraform applyWhat Terragrunt adds is a layer that manages the repeated boilerplate you just saw pile up around calling Terraform once per environment:
- Generating backend configuration from one central definition instead of copy-pasting the same
backend "gcs" { ... }block into every environment directory. - Generating provider configuration the same way, from one place, instead of a near-identical
provider "google" { ... }block per environment. - Supplying each environment’s input variables to the shared module so
dev,staging, andprodstill call onemodules/appsource with different values, without hand-wiring a separatemoduleblock per environment. - Understanding dependency ordering when infrastructure is split across multiple independently-applied units — for example, a
networkunit that agkeunit depends on — so Terragrunt can figure out thatnetworkneeds to apply beforegkerather than you tracking that ordering by hand across separateterraform applyruns.
That last point matters specifically because directory-per-environment splits infrastructure into genuinely separate state files per environment (and, as the next module covers, often per component too). A single Terraform configuration has its own internal dependency graph between resources in one state file, but it has no built-in concept of ordering across entirely separate configurations and separate terraform apply invocations — that cross-configuration ordering is exactly the kind of problem Terragrunt is built to solve, on top of Terraform’s own graph, not instead of it.
What Terragrunt is not
Section titled “What Terragrunt is not”It is worth being precise about the boundary here, since it is easy to overstate what Terragrunt does:
- Terragrunt is not a fork of Terraform, and it is not a replacement for it — the actual planning and applying of resources is still done entirely by
terraformortofu. - Terragrunt is not a new configuration language for describing GCP resources — a
google_compute_instanceresource is defined exactly the same way whether or not Terragrunt is involved. - Terragrunt cannot make an unsafe Terraform change safe on its own — it removes copy-pasted boilerplate and orchestrates ordering, but the actual infrastructure logic and its correctness are still entirely your
.tfmodule code’s responsibility.
The next module picks up exactly here, with the concrete Terragrunt building blocks — root.hcl, remote_state, generate, and a unit’s own terragrunt.hcl — that turn this positioning into a working setup.
flowchart TD workspaces["Terraform workspaces: DRY, weak isolation"] --> gap["The gap: isolation AND zero duplication together"] directoryPerEnv["Directory-per-environment: isolated, duplicated boilerplate"] --> gap gap --> terragrunt["Terragrunt: generates backend, provider, inputs, manages unit ordering"] terragrunt -->|shells out to| terraformBinary["Real terraform / tofu binary"]