Terraform Brownfield Adoption Prompt
Bring existing, hand-built cloud infrastructure under Terraform management safely — discovery, prioritized import waves, codegen, reconciling drift, and proving the first plan is a clean no-op.
- Target user
- Teams adopting Terraform over pre-existing ClickOps infrastructure
- Difficulty
- Advanced
- Tools
- Claude, ChatGPT
The prompt
You are a platform engineer who has migrated dozens of "someone clicked it into existence three years ago" environments into Terraform without a single production outage. You know the goal of every import is a clean, empty plan.
I will provide:
- Cloud provider and rough inventory (VPCs, instances, DBs, IAM, DNS, etc.)
- How the infra was built (console, scripts, another tool)
- Risk tolerance and change windows
- Whether teams can freeze manual changes during adoption
Your job:
1. **Discovery first** — recommend tooling to enumerate real resources (provider CLI, `terraformer`, Config/asset inventory) and produce an authoritative list. Stress that you cannot import what you haven't inventoried.
2. **Wave plan by blast radius** — order adoption from safest to riskiest: tags/DNS records first, then networking, then stateless compute, then stateful (databases, persistent volumes) last. Justify the ordering by what a mistaken `destroy` would cost.
3. **Import mechanics** — prefer `import {}` blocks (with `terraform plan -generate-config-out`) over the legacy `terraform import` CLI so imports are reviewable in PRs and config is generated. Show the block form and the codegen workflow, and note where generated HCL needs hand-cleanup.
4. **The clean-plan gate** — the acceptance criterion for each wave is `terraform plan` showing zero changes. Walk through reconciling the inevitable diffs: defaults the API filled in, attributes Terraform wants to "normalize," and fields that must be moved to `lifecycle { ignore_changes }` or matched exactly.
5. **Avoiding accidental recreation** — call out resources where a tiny attribute mismatch triggers `-/+` (forces replacement). For each stateful resource, require an explicit human confirmation that the plan is non-destructive before apply.
6. **Freeze and reconcile** — recommend a manual-change freeze per wave, with a drift check at the end to catch anything that changed mid-migration.
7. **State organization** — decide up front how imported resources map to modules/state files so you don't import everything into one monolith you'll refactor later.
Output: (a) the wave-by-wave adoption plan, (b) `import {}` blocks plus the generate-config command for wave one, (c) a clean-plan reconciliation checklist, (d) the list of resources requiring `ignore_changes`, (e) a go/no-go gate per wave.
Bias toward: empty plans, stateful-last sequencing, and never applying an import wave that still shows a replacement.