pa-07 — Infrastructure as Code (Terraform / Pulumi model)
The Apple JD wants familiarity with "infrastructure-as-code tools, such as Terraform or Pulumi." Under both sits one engine: take a declarative description of desired infrastructure, diff it against the current state, and converge the real world to match — in dependency order, idempotently, with drift detection. Build that engine once and Terraform/Pulumi stop being magic.
You build it: resources with a dependency DAG, plan (the diff), apply (topological convergence), persistent state, idempotent re-apply, and drift detection.
1. What is it?
Infrastructure as Code manages infrastructure (networks, DBs, clusters, DNS) through machine-readable declarative definitions rather than manual clicks or imperative scripts. You declare what you want (the desired state); the engine figures out how to get there.
The engine's loop:
desired config ─┐
├─▶ PLAN (diff desired vs state) ─▶ create/update/delete/no-op
current STATE ──┘ │
▼
APPLY (in dependency order) ─▶ converge real world + update STATE
│
DRIFT: state vs the actual world (changed out of band)
- Declarative, not imperative. You say "a VPC and a subnet in it exist," not "if no VPC, create one, then…". The engine computes the delta.
- Dependency graph. Resources declare
depends_on; the engine builds a DAG and applies in topological order (the VPC before the subnet before the DB). - State. The last-applied snapshot, so the next plan knows what to diff against (Terraform's state file — and a notorious source of pain).
- Idempotent. Applying the same config twice changes nothing.
- Drift. The world can change outside the tool (a manual edit); drift detection finds where reality diverged from state.
2. Why does it matter?
-
It's how a platform is reproducible and reviewable. Infra-as-code means environments are versioned in git, peer-reviewed (a testing strategy, pa-10), diffable before apply (plan), and rebuildable from scratch. "Click-ops" is the opposite: unreproducible, unauditable, drift-prone.
-
Declarative + reconcile is the dominant control paradigm. This exact loop — desired vs actual, converge — is Kubernetes (gw-10), GitOps (pa-08), and xDS (gw-08). An architect who sees that all four are the same idea wields it everywhere. (db-17's "drive replicas to a desired state" is the same instinct.)
-
The dependency DAG and ordering are where correctness lives. Create a subnet before its VPC and apply fails; delete a VPC before its subnet and you orphan or error. Topological order (and reverse for deletes) is the non-obvious engine detail; cycle detection prevents impossible configs.
-
State and drift are the operational hazards. A stale or corrupt state file, or undetected drift, causes the engine to plan the wrong delta (recreate live resources, or miss out-of-band changes). Knowing these failure modes is the difference between using Terraform and operating it.
3. How does it work?
Plan (the diff)
Plan(desired, state) validates dependencies exist, checks for cycles
(topoSort), then for each desired resource: Create (not in state),
Update (in state but attributes differ — with a diff), or NoOp
(unchanged). Resources in state but not desired → Delete. Plan is
read-only — you review it before committing. That preview is a core
safety feature: you see "this will destroy the prod DB" before it
happens.
Apply (converge, in order)
Apply runs the plan, executing creates/updates in dependency order
(deps first) and deletes in reverse order, then updates state. The
topological sort (Kahn's algorithm, deterministic) is what guarantees the
VPC exists before the subnet that needs it.
Idempotency
Applying the same config twice yields all NoOps — because the second plan diffs desired against a state that already equals it. Idempotency is what makes IaC safe to run repeatedly (in CI, on a schedule) and is the same property as the reconcile loops in gw-08/gw-10/pa-08.
State and drift
State is the last-applied snapshot. Drift = the actual world (live)
differs from state: someone hand-edited a resource, or an unmanaged
resource appeared. Drift(state, live) reports both. The next apply
would "correct" managed drift back to the declared config — which is
exactly what GitOps self-heal (pa-08) automates continuously.
Modules, providers, and the real complexity
Real IaC adds providers (plugins that talk to AWS/GCP/k8s), modules (reusable resource bundles), variables/outputs, and remote state with locking (so two engineers don't apply concurrently and corrupt state). The engine here is the kernel those wrap.
4. Core terminology
| Term | Definition |
|---|---|
| Declarative | Describe desired state; the engine computes the steps. |
| Plan | The read-only diff (create/update/delete/no-op) before applying. |
| Apply | Converge the real world to desired, in dependency order. |
| State | The last-applied snapshot the next plan diffs against. |
| Dependency DAG | Resource graph; topo order = apply order (reverse for deletes). |
| Idempotent | Re-applying the same config changes nothing. |
| Drift | The actual world diverging from state (out-of-band change). |
| Provider | A plugin that creates/reads/updates a real resource type. |
| Module | A reusable, parameterized bundle of resources. |
| State lock | A mutex preventing concurrent applies from corrupting state. |
5. Mental models
-
IaC is
gitfor infrastructure. You commit a desired state;planisgit diff;applyis the merge that makes reality match. State is the index that tells you what's already committed. Drift is "someone edited the deployed files directly instead of through git." -
Declarative is a thermostat; imperative is flipping the heater. You set the target (68°F / "these resources exist") and the engine drives toward it from wherever it is. An imperative script ("turn the heater on") breaks if the room's already warm or the heater's already on; the declarative engine just no-ops.
-
The dependency DAG is a recipe's ordering. You can't ice the cake before baking it. Topological sort is reading the recipe to find a valid order; a cycle ("A needs B, B needs A") is a recipe that can't be cooked.
-
State is load-bearing and fragile. It's the engine's memory of the world. Lose it and the engine thinks nothing exists (and tries to recreate everything); corrupt it and it plans nonsense. Remote state + locking exist because this single file is the system's truth.
6. Common misconceptions
-
"IaC is just scripts that create infra." Scripts are imperative and not idempotent (re-running breaks or duplicates). IaC is declarative with a diff-and-converge engine — that's what makes it safe to re-run and reviewable via plan.
-
"Apply order doesn't matter." It's everything: dependencies dictate create order and the reverse for deletes. Get it wrong and you create orphans or fail. The DAG + topo sort is the engine's core.
-
"State is just a cache." It's the source of truth for what the tool manages. A wrong state file makes the engine destroy or recreate live infrastructure. Treat it like a database: remote, locked, backed up.
-
"Plan == apply." Plan is read-only and is your safety review; apply mutates the world. Always read the plan (especially the destroys). In CI, gate apply on a reviewed plan.
-
"Drift doesn't happen if everyone uses the tool." Someone always hand-fixes prod at 3am. Drift detection (and GitOps self-heal, pa-08) is what catches and reverts it — or at least surfaces it.
7. Interview talking points
-
"How does Terraform/Pulumi work under the hood?" Declarative resources + a dependency DAG → a plan (diff desired vs state) → apply in topological order → updated state; idempotent re-apply; drift detection vs the live world. Name state-file fragility and locking. This is the engine, and you can say you've built it.
-
"Why declarative over imperative scripts?" Idempotency (safe to re-run), a reviewable diff (plan) before changes, reproducibility from git, and convergence from any starting state. Scripts are none of these.
-
"Where does apply ordering come from?" The dependency DAG, topologically sorted (deps first for create/update, reverse for delete). Cycles are rejected. This is the same ordering concern as ADS in xDS (gw-08) and pa-01's build order.
-
"What is state and why is it dangerous?" The last-applied snapshot the engine diffs against. Lost/stale/corrupt state → the engine plans the wrong delta (recreates live resources, misses changes). Mitigate with remote state + locking + backups +
importfor adopting existing resources. -
"How do you handle drift?" Detect it (compare state to the live world) and either re-apply to converge or alert. GitOps (pa-08) automates this as continuous self-heal. The same desired-vs-actual loop as Kubernetes (gw-10).
-
"Terraform vs Pulumi vs Crossplane?" Terraform/Pulumi: external engine + state file (Pulumi uses real languages instead of HCL). Crossplane: the reconcile loop inside Kubernetes (operators, gw-10). Same model; different host for the loop.
8. Connections to other labs
- gw-08 / gw-10 (xDS / operators) — the same declarative desired-vs-actual reconcile loop, hosted in a control plane / k8s instead of an external CLI.
- pa-08 (GitOps) — git is the desired state; a reconciler runs this plan/apply continuously and self-heals drift.
- pa-01 (decomposition) — the dependency DAG + topological order + cycle detection is the same machinery as the service graph.
- db-17 (Raft) — "drive the system to a replicated desired state" is the consensus instinct behind every reconcile loop.