Terragrunt
Terragrunt is a thin wrapper around OpenTofu that generates the repetitive parts of every configuration, wires units together through their outputs and runs many of them in dependency order. The Azure Enterprise Baseline and the Azure Web App and Data and ETL Blueprints pin Terragrunt 1.1.6 and use its Stacks layout.
What it does
A unit is one terragrunt.hcl: one module, one state, its inputs. A stack file (terragrunt.stack.hcl) lists units, or other stack templates, with values, and terragrunt stack generate writes the units into a .terragrunt-stack/ folder. A dependency block reads another unit's outputs; mock outputs stand in for them during validate and plan before that unit exists. generate blocks write files such as backend.tf and provider.tf into every unit, and run --all runs a command over a whole tree in dependency order. A feature flag is a typed switch that can be set per run.
How BuiltForProd uses it
Layout. Every environments/<group>/<name>/<region or global>/ folder holds one terragrunt.stack.hcl, and the units it lists come from units/. Generated units land in .terragrunt-stack/, which is git-ignored and never edited: a change goes into units/, stacks/ or a stack file and is regenerated.
| Repository | Templates | A stage or subscription file holds |
|---|---|---|
| Azure Enterprise Baseline | stacks/plat-subscription, with includes/subscription-common and includes/subscription-link | The units of each core subscription; the template for each plat subscription |
| Web App Blueprint | stacks/webapp-stage, 13 units in shared-infra/ and blueprint-app/ | Only values: SKUs, node counts, data-store sizes, sync mode |
| Data and ETL Blueprint | stacks/etl-stage, 5 units in shared-infra/ and blueprint-etl/ | Only values: lake replication, Spark sizing, trigger sizing |
root.hcl. Each repository's root file reads common.hcl, region.hcl and account.hcl, builds the names and tags, and generates three files into every unit: backend.tf (the shared state account, a key per unit path), versions.tf (the only provider pins) and provider.tf. The default azurerm provider targets the unit's own subscription; aliases let a unit write into another one:
| Repository | Aliases |
|---|---|
| Azure Enterprise Baseline | root, hub (core-network), dns, auto, security, audit |
| Web App Blueprint | network, dns, auto, artifacts |
| Data and ETL Blueprint | None: everything it needs from the landing zone comes from the contract vault |
The Baseline's root.hcl also holds one map of the hub units' generated paths, so a dependency on, say, the audit workspace is written as include.root.locals.hub.audit_workspace and the folder name lives in one place.
Dependencies. Every dependency carries complete mock outputs for validate and plan, which scripts/check-mock-outputs.py enforces; an apply always uses real outputs. Two Terragrunt feature flags, security_workspace in the subscription baseline and platform_services in kv-publish, can switch a dependency off and default to on.
Running it. CI runs terragrunt run --all --working-dir environments -- plan over the Baseline and one stage folder at a time in the blueprints, and uploads Terragrunt's JSON run report as an artifact. Locally the same commands run from a stack folder. See GitHub Actions for the workflows.
Terms you will see
| Term | Meaning |
|---|---|
| Unit | One module with its inputs and its own state. |
| Stack file | terragrunt.stack.hcl, the list of units for one folder. |
| Stack template | A reusable stack such as webapp-stage, filled with a stage's values. |
| Generated tree | .terragrunt-stack/, written by stack generate, never edited. |
| Provider alias | A second azurerm provider that targets another subscription. |
| Mock outputs | Placeholder outputs that let a unit plan before its dependency exists. |
Where to read more
- Azure Enterprise Baseline overview for the repository layout.
- Terragrunt units and stacks for the concept.
- OpenTofu for the engine and its providers.