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.
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

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.