Lesson 5 · This repo's IaC: Terragrunt & the env×org matrix

Terragrunt & DRY

The wrapper that keeps ~180 configs from copy-pasting the same backend block 180 times.

Your win: explain what Terragrunt adds over plain Terraform, and read this repo's root.hcl + a leaf config — the DRY machinery behind the whole terraform/ tree.

The repetition plain Terraform leaves you with

Modules (Lesson 4) DRY up the resources. But every place you run Terraform still needs its own backend config (which state bucket + key) and provider config — and in a repo with ~180 places to run it, you'd copy that same boilerplate 180 times, differing only by the state key.1 Terragrunt is a thin wrapper that fixes exactly this: define the backend + provider once, and every leaf inherits it.

The root config — write it once

Anchor — deployments/terraform/live/root.hcl (Note: named root.hcl, not the conventional terragrunt.hcl.) It holds the shared config for every leaf:

A leaf — thin, and it just includes the root

Anchor — a leaf (live/stag-manabie/platforms/terragrunt.hcl) Each component config is tiny and follows one shape: That's the whole grammar. include for the shared config, source for the module, dependency for cross-component outputs. Every one of the ~180 leaves is built from this.
The pattern in one line Terragrunt = "DRY the backend/provider once (root.hcl), then each leaf just includes it and points at a module." Plain Terraform makes you repeat the backend block everywhere; Terragrunt's generate + include generate it for you. It's the same "write once, stamp out many" instinct as modules — applied one level up, to the wiring instead of the resources.
Backend use case Backend use case: this is the real lens for `live/root.hcl`, generated provider/versions blocks, and the thin leaf configs that point at modules and wire dependencies.
Common mistake Common mistake: describing Terragrunt as if it replaces Terraform instead of recognizing it as a wrapper that standardizes and scales how Terraform is invoked.
Read this next

Terragrunt — Keep remote state DRY + config blocks

The remote_state/generate/include blocks and the DRY rationale.

terragrunt — Keep remote state DRY
terragrunt — Config blocks & attributes

In plain English Plain English: Terragrunt keeps repeated Terraform wiring DRY so the repo does not duplicate backend and provider boilerplate hundreds of times.

Check yourself (from memory)

Q1. Terragrunt's main job over plain Terraform is to…

Define the backend + provider once in the root; each leaf includes it — no 180× copy-paste.

Q2. In this repo, a leaf points at the module to instantiate via…

The leaf's terraform.source names the module; include brings the root config, dependency the cross-component outputs.

Q3. The repo's Terragrunt root config file is named…

Here it's root.hcl (not the conventional terragrunt.hcl); leaves find_in_parent_folders("root.hcl").
What Terragrunt adds, and this repo's root + leaf shape.
recall, then click to reveal
Modules DRY the RESOURCES; plain Terraform still repeats BACKEND + PROVIDER config in every dir (~180× here). TERRAGRUNT = a thin wrapper: define them ONCE in the root, each leaf INCLUDES it. REPO ROOT = live/root.hcl (NOT terragrunt.hcl): remote_state gcs backend + per-leaf prefix path_relative_to_include() (:11-23), generate "provider" (:25), generate "versions" (TF≥1.9, :39), inputs = merge(env, common) (:57). A LEAF (…/platforms/terragrunt.hcl): include { find_in_parent_folders("root.hcl") } + terraform { source = "…/modules/platforms" } + dependency "vpc" {} (outputs→inputs) + include "env" (shared logic, expose=true). Grammar: include=config, source=module, dependency=wiring.
Want to see how path_relative_to_include() gives each leaf a unique state key, or what run-all does across leaves? Ask me.

1. Terragrunt — Keep your remote state configuration DRY.