Lesson 0008 · Understand Terraform · Capstone

The modern workflow

Everything composes · ~15 min. Config-driven lifecycle ops, plan-as-artifact, drift on a schedule — "what modern Terraform looks like and how people use it."
Why last? This was your original question, verbatim: "what modern Terraform looks like and how people use it." Every piece is now in your toolkit — this lesson assembles them into the day-to-day shape, and the capstone quiz deliberately reaches back across all seven lessons.

Modern Terraform's signature move: state surgery became code. The imperative commands you learned as escape hatches each grew a declarative, reviewable twin:

The config-driven trio

moved   { from = aws_s3_bucket.logs, to = module.storage.aws_s3_bucket.logs }  # 1.1 — rename (lesson 4)

import  { to = aws_s3_bucket.legacy, id = "acme-legacy-data" }                 # 1.5 — adopt existing
# + terraform plan -generate-config-out=generated.tf drafts the resource block from reality

removed { from = aws_db_instance.old
          lifecycle { destroy = false } }                                      # 1.7 — forget (lesson 2)

Same surgeries as state mv / import / state rm — but they ship in a PR, show up in a plan before touching state, and replay identically for every teammate and pipeline. The imperative commands remain for emergencies.

The team loop

Pre-commit: fmt → validate → terraform test (unit layer). PR: CI runs plan -out=tfplan; humans review the plan, not just the diff. Merge: CI runs apply tfplan — executing exactly the reviewed plan. If the world changed in between, the apply errors instead of improvising. Plan-as-artifact is the three-pictures model from lesson 0001 turned into a governance mechanism.

On a schedule: plan -detailed-exitcode — exit 0 clean, 2 means "the pictures disagree" → alert; triage with plan -refresh-only to split drift from pending work (lesson 2).

Two ecosystem facts complete the picture. Pin everything — required_version, provider ~> constraints, module versions — and commit .terraform.lock.hcl (exact provider versions; the one state-adjacent file that belongs in git). And know OpenTofu: when Terraform's license went BUSL in 2023, the community forked the last open version into OpenTofu (Linux Foundation) — drop-in compatible today, diverging slowly. Feature floors worth memorizing: moved 1.1 · import block 1.5 · test 1.6 · removed/mocks 1.7 · use_lockfile 1.10.

The capstone — everything is fair game

✅ The final proof (optional, 30 min — the full loop, no AWS required)

  1. Take your lesson-5 layout (modules/ + envs/dev + envs/prod) and add a tests/ suite to the module (lesson 7).
  2. Create a resource outside Terraform (even a local file), adopt it with an import block + -generate-config-out.
  3. Refactor it into the module with a moved block; verify the plan shows a move, zero changes.
  4. Retire it with a removed block (destroy = false); verify it survives while leaving state.
  5. Wire a script: fmt -check && validate && test && plan -detailed-exitcode; echo $? and read the exit code like CI would.
💬 You've finished the course — what now? Ask me to: quiz you across all 8 lessons (spaced repetition) · design a real project to build · go deeper on anything (Terratest, Terragrunt, provider internals, OpenTofu divergence) · or pressure-test your understanding with harder war-game scenarios. Wisdom comes from practice: r/Terraform and the HashiCorp forum are where real design debates happen — lurk, then answer one question a week.