# Cheat sheet — Infrastructure as Code

Dense revision sheet. `terraform`/`terragrunt` commands + interview one-liners at the bottom.

---

## The model
- **IaC** = infrastructure as version-controlled code — reviewable, diffable, reproducible.
- **Terraform** is **declarative**: you write desired state (HCL); it diffs against **state** and
  applies. Same idea as Kubernetes, for cloud resources.
- **Core loop:** write → `plan` (preview the diff) → `apply` (make it so).

## Building blocks
| Thing | What |
|---|---|
| **Provider** | plugin to a platform's API (`google`, `cloudflare`, `github`) |
| **Resource** | one piece of infra (`google_container_cluster`, `google_service_account`) |
| **Variable / output** | a module's inputs / return values |
| **Module** | reusable, parameterized package of resources |
| **State** | Terraform's record of real infra — the source of truth it diffs against |
| **Backend** | where state lives (here: **GCS**) — shared, versioned, locked |

## State
- **Remote state** (GCS) so a team + Atlantis share one truth; **locking** (GCS-native) stops
  concurrent applies. **Drift** = reality ≠ state (someone clicked the console); `plan` detects it.
- Repo: bucket `manabie-terraform-state-2` (default), per-component prefix = isolated state.

## Terragrunt (DRY)
- Wraps Terraform: define `remote_state` + `generate "provider"` **once** in the root config;
  each leaf `include`s it. Repo root = **`root.hcl`** (not `terragrunt.hcl`).
- **`live/` vs `modules/`**: `modules/` = reusable defs (no state); `live/` = thin per-cell configs
  that instantiate modules with real values (`terraform { source = "…/modules/x" }`).
- Cross-component wiring via `dependency "x" { }` (outputs → inputs).

## The env×org matrix (as a directory tree)
```
live/
  root.hcl                         # remote_state + provider, once
  _env/<component>.hcl             # shared component logic (expose=true)
  <env>-<org>/                     # e.g. stag-manabie, prod-tokyo
    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
  cloudflare/  github-oidc/  kms-key/  terraform-state-bucket/   # global (not env-org)
```
- 34 modules × ~180 `terragrunt.hcl` leaves. One cell+component = one isolated state.

## What Terraform provisions here
- **GKE** (`modules/platforms/gke.tf`): spot vs on-demand node pools (`spot=true/false`, Course 2),
  Workload Identity, `enable_binary_authorization` (`:111`).
- **Cloud SQL** Postgres (lms/common/auth/lmsdwh/aitutor); **VPC**/NAT; **Cloudflare** DNS + edge
  certs (Course 4); **GCS** (TF state, CI cache, platform buckets — but NOT `manabie-thanos`).
- **IAM / Workload Identity** — the cross-course payoff (below).
- **KMS** — SOPS keys (keyring `backend-services`) + binauthz keys (keyring `binauthz-attestors`).

## Workload Identity — the payoff (ties C3/C5/C6)
- **Per-service (GKE):** a GCP SA (`google_service_account`) + a binding
  `google_service_account_iam_member` role `roles/iam.workloadIdentityUser`, member
  `serviceAccount:PROJECT.svc.id.goog[<ns>/<ksa>]`. **This is the Terraform behind the
  `iam.gke.io/gcp-service-account` annotation** (Course 3). No static keys.
- **GitHub CI (WIF):** a `google_iam_workload_identity_pool` (`gh-action-pool`) + providers; bots
  (`prod-build-bot`, …) bound by `principalSet://…/attribute.repository/<repo>` (Course 5).

## Apply path — Atlantis (not GitHub Actions)
- Open a TF PR → Atlantis posts `terragrunt plan`. Comment **`atlantis apply`** → `terragrunt
  apply` (state-locked). `apply_requirements: [approved]`; only the **terraform-approvers** team
  may apply. (CI `paths:` deliberately skip `deployments/terraform/**`.)

## terraform / terragrunt kit
```
terraform init            # download providers, configure backend
terraform plan            # preview the diff (create/change/destroy)
terraform apply           # make the changes (prompts / -auto-approve)
terraform state list      # what's tracked
terraform import ADDR ID  # adopt existing infra into state
terragrunt run-all plan   # plan across many leaves
# in a PR:  atlantis plan   /   atlantis apply
```

## Interview one-liners
- **What is IaC?** *"Infrastructure defined as version-controlled code — you write desired state,
  the tool diffs it against real state and applies. Reviewable, reproducible, auditable."*
- **What is Terraform state, and why remote?** *"State is Terraform's record of real infra — the
  source of truth it diffs against. Remote (GCS) so a team shares one state with locking; local
  state doesn't scale to a team."*
- **What is drift?** *"When reality no longer matches state — a console click. `plan` shows it;
  `apply` or `import` reconciles it."*
- **Why Terragrunt?** *"Keeps configs DRY — define the backend + provider once, each cell
  includes it. We have ~180 leaf configs across the env×org matrix; without it you'd copy the
  backend block into every one."*
- **How does a Pod get GCP access without a key?** *"Workload Identity — a Terraform binding
  (`roles/iam.workloadIdentityUser`) maps the KSA to a GCP SA; the pod's annotation points at it.
  That binding is IaC."*
- **How do Terraform changes ship?** *"Through Atlantis on the PR — `atlantis plan` on open,
  `atlantis apply` on comment, gated by approval + the terraform-approvers team. Not the app
  CI/CD."*
