Terraform¶
Core · prereq 03 (Containers/Podman) · ~1.5–2 days (4 submodules: state, modules, providers/lifecycle, CI)
A shell script that provisions a server works today and fails next month — it assumed a world (no resource yet, no name clash) that's no longer true once you've already run it. Terraform is declarative instead: you write the infrastructure you want, and terraform plan computes exactly what would change against a state file that remembers what's actually out there — every time, before anything touches the cloud. Nothing applies until you've read that diff.
TL;DR — in 30 seconds:
- Terraform is declarative:
planalways computes a diff against state — a record of what's actually out there — before anything touches the cloud; nothing applies until you've read that diff. - State is the third file that closes the loop between what your config says you want and what the
cloud actually has; without it,
plancan't tell "this already exists" from "this is brand new." - A module is just a directory of
.tffiles with its own inputs (variable) and outputs — the same shape (e.g. a VPC) can be reused across environments instead of copy-pasted and left to drift.
Learning objectives¶
By the end, the junior can:
- Write HCL with resources, variables, outputs, and run plan → apply.
- Explain what state is and why it matters.
- Factor a small module and read a plan diff.
Pair, Do and Prove run in Claude Code, with the onboarding-tutor skill. Click a button to copy the exact prompt, then paste it into Claude Code in this repo.
1. Learn · acquire the concept¶
Read: the terraform course — focus §4 (HCL essentials), §5 (first resource), §6 (variables/outputs/locals), §9 (modules), §11 (state). Skip for now: §10 (providers in depth) and the S3 backend specifics — you'll use those on real infra later. You've met IaC before: module 05 (AWS) introduced it as CloudFormation/CDK — this is the cloud-agnostic, state-centric take. Glossary (beat 5): the module’s Anki deck Read next: Terraform documentation (official docs — full configuration-language and CLI reference).
Check yourself — you run terraform plan on a config you haven't touched in months. What is it actually comparing to decide there's nothing to do?
Config against state (refreshed against the real cloud), not against your last apply's output — plan has no memory of "last time" beyond what's recorded in state.
Check yourself — why does a shell script that provisions a server 'work today, fail next month,' while a second terraform apply on an unchanged config reports no changes?
The shell script has no record of what it already created, so it re-runs its create logic blind to the current world. Terraform's plan diffs config against state first, so a second apply against unchanged infrastructure computes zero changes instead of trying to recreate anything.
2. Pair · passive → active¶
Drive the onboarding-tutor skill:
- "Explain declarative vs imperative with a Terraform example."
- "3-line mental model of what
terraform.tfstateis and why it's dangerous to lose." - "Quiz me on plan vs apply before I run anything."
Common gotchas
- Running
applywithout reading theplanoutput first. Fix: always read what+/~/-/-/+mean for each line — a-/+under a stateful resource is the single most common "oh no" in review; catch it before approving, not after. - Treating
terraform.tfstateas disposable. Fix: it's the only record mapping your config to the real-world resources — losing it (or letting two applies race without a lock) means Terraform re-guesses "already exists" vs "brand new," usually by trying to recreate everything. - Copy-pasting the same HCL into a second environment instead of factoring a module. Fix: the
moment one copy gets a fix the other doesn't, they drift — a module's
variableinputs are the difference between one reusable shape and two configs slowly diverging. - Assuming
countandfor_eachare interchangeable ways to make N copies. Fix:for_eachkeys each instance by identity so removing one doesn't shift or recreate the rest;countshifts every index above a removed element.
Check yourself — a plan shows -/+ on a resource you only meant to rename in code, not change. What went wrong?
The rename likely changed the resource's config address (its resource label or module path), not
just a cloud attribute — Terraform doesn't recognize the new address as "the same resource," so it
defaults to destroy-and-recreate. A moved block would tell it this is the same resource at a new
address instead.
Check yourself — you factor a working root config into a module. What can the module see from the caller, and what can the caller see from the module, after the split?
Only what crosses the interface: the module sees whatever the caller passes as variable inputs, and
the caller sees only what the module exposes as outputs. A local or a bare resource attribute on
either side never leaks across — encapsulation, not convention.
Done when: you can explain plan/apply and state's role, unprompted.
3. Do · produce an artifact¶
Exercise — a safe, cloud-free first module:
Use a local/safe provider so a junior can't break anything real — e.g.
local_file,random, or anull_resource. (No cloud credentials.)
- Install Terraform first (course §3):
brew install terraform. - Write a config with a variable, a resource, and an output.
terraform init→plan→apply. Read the plan carefully before applying.- Inspect
terraform.tfstateand find your resource in it. - Change the variable,
planagain, and read the diff (what will change and why). - Factor the resource into a small module and call it from root.
Done when: apply succeeds, you can point to your resource in state, and you've read a change-plan diff.
4. Prove · understanding gate¶
Mandatory. Pass bar in the Socratic gate.
- Walkthrough: Explain your config and the
planoutput line by line — what does+,~,-mean? - Alternative:
countvsfor_eachfor multiple instances — when each? - Failure & debug: Someone deletes the resource by hand (drift). What does the next
planshow, and how does Terraform reconcile it? What happens if you lose state? - Provenance: Which HCL did the agent write? How did you verify the plan does what you intended (not just that it applied)?
Pass bar: you can read a plan, explain state, and reason about drift. "It applied" without reading the plan = back to Pair.
5. Retain · fight forgetting¶
Study the module deck SE Onboarding::07 Terraform — its 14 theme subdecks let you drill one area at a time. It syncs to your Anki automatically on pull (anki-sync).
Done when: you can recall — from the deck — resource, provider, state, plan/apply, variable/output, module, and drift.
Key takeaways¶
- Terraform is declarative:
planalways diffs config against state before touching the cloud — nothing applies until you've read that diff. - State is what lets
plantell "already exists" from "brand new." Lose it, or race two applies without a lock, and Terraform re-guesses — usually by trying to recreate everything. - A module is a directory of
.tffiles with its ownvariableinputs andoutputs. Encapsulation is real: the module only sees what the caller passes in, the caller only sees what the module exposes. - A rename without a
movedblock reads as a new resource address, not "the same resource renamed." Terraform defaults to destroy-and-recreate (-/+) instead of an in-place rename. for_eachkeys each instance by identity, so removing one leaves the rest untouched;countshifts every index above a removed element — which can recreate resources you never meant to touch.- Reading the plan symbols (
+ ~ - -/+) before approving is the whole safety model. "It applied" without reading the diff means back to Pair, not Prove.
Go deeper — curated resources¶
Hand-picked external material to take this topic further — the best books, courses, talks, and writing. Cost is tagged Free, Free online (full text/course free on the web), or Paid.
- Book · Terraform: Up & Running — Yevgeniy Brikman. The canonical Terraform book, from the founder of Gruntwork. Paid.
- Course · HashiCorp Developer — Terraform tutorials — HashiCorp. Official, hands-on learning tracks from first steps to production. Free.
- Docs · Terraform Documentation — HashiCorp. The full configuration-language and CLI reference. Free.
- Docs · Terraform Registry — HashiCorp. The catalog of providers and reusable modules. Free.
- Blog · Gruntwork Blog — Gruntwork. Deep infrastructure-as-code patterns from Brikman's team. Free.
Gate log (mentor fills on pass)¶
- Date passed: / Passed by: / checked by: / Weak spots: