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

The env×org matrix as a directory tree

The same matrix from Course 1 — now expressed as folders, each with its own state.

Your win: read the live/ directory tree and explain how the env×org matrix (Course 1) becomes one directory per cell per component, each with isolated state — and how shared logic stays DRY.

The matrix you already know, as folders

Course 1 introduced the org's organizing principle: environment × organisation (local × manabie, prod × tokyo, …). You've seen it as Helm values files (C3) and Kubernetes namespaces (C2). Here it's a directory tree under live/ — one folder per cell, one sub-folder per component:

live/ ├── root.hcl # backend + provider, once (Lesson 5) ├── stag-manabie/ │ ├── env.hcl # project_id, region, env, state bucket │ ├── platforms/terragrunt.hcl # → modules/platforms (GKE, Cloud SQL, GCS, KMS) │ ├── apps/terragrunt.hcl # → modules/apps (GSAs + Workload Identity) │ └── binary-authorization-attestor/terragrunt.hcl ├── prod-tokyo/ env.hcl platforms/ apps/ … ├── prod-jprep/ … └── _env/ # shared component logic (below)
Anchor — three layers of DRY The env×org config is factored into three levels:
  1. Per-cell valueslive/stag-manabie/env.hcl holds that cell's specifics: project_id: "staging-manabie-online", region: "asia-southeast1", env: "stag", service_account_prefix: "stag-", and the state bucket.
  2. Shared component logiclive/_env/*.hcl (e.g. platforms.hcl, stag-apps.hcl, project-roles.hcl) is included by leaves with expose = true, so the same component logic isn't rewritten per cell.
  3. Global (not per-cell) dirslive/cloudflare, github-oidc, kms-key, terraform-state-bucket, monitoring — things that exist once for the org, not per env×org.
One cell + component = one isolated state Because the backend prefix is path_relative_to_include() (Lesson 5), each leaf's directory path is its state key. So stag-manabie/platforms and prod-tokyo/platforms have completely separate state in the shared bucket. Blast-radius win: a mistaken apply in one cell can't touch another's infrastructure. This is the env×org isolation from Course 1, enforced at the state layer.
Scale: 34 modules, ~180 leaves Count it up: ~34 module definitions, instantiated across ~30 cells × several components = ~180 terragrunt.hcl leaves. Each leaf is thin (Lesson 5) because the module + root + _env do the heavy lifting. That's how a small team manages a large, multi-tenant, multi-environment cloud footprint without drowning in copy-paste.
Read this next

Terragrunt — config blocks (include / dependency / expose)

How include, dependency, and expose compose the per-cell tree.

terragrunt — Config blocks & attributes
→ In-repo: deployments/terraform/live/

Check yourself (from memory)

Q1. In this repo, the env×org matrix is expressed as…

One folder per cell (stag-manabie, prod-tokyo) × one sub-folder per component — the C1 matrix as folders.

Q2. A cell's specifics (project, region, state bucket) live in…

Per-cell env.hcl locals; shared logic in _env/*.hcl; global things in live/cloudflare etc.

Q3. stag-manabie/platforms and prod-tokyo/platforms have…

The backend prefix is the leaf's path, so each cell+component is isolated — a mistake in one can't touch another.
The env×org matrix as a directory tree, and its three DRY layers.
recall, then click to reveal
The C1 env×org matrix (env × org) becomes a DIRECTORY TREE under live/: one folder per CELL (stag-manabie, prod-tokyo) × one sub-folder per COMPONENT (platforms, apps, …), each a thin terragrunt.hcl. THREE DRY LAYERS: (1) per-cell env.hcl (project_id, region, env, service_account_prefix, state bucket); (2) shared component logic in live/_env/*.hcl (included with expose=true); (3) global dirs not per-cell (cloudflare, github-oidc, kms-key, terraform-state-bucket). Because the state prefix = the leaf's PATH, each cell+component has ISOLATED state (blast-radius win). Scale: 34 modules → ~180 leaves.
Want to see how a new org would be added (a new cell dir), or which things are global vs per-cell? Ask me.

1. In-repo: deployments/terraform/live/; Terragrunt — Config blocks.