Two versions of the same problem: infrastructure reality has diverged from code (drift), or reality was never in code at all (brownfield/unmanaged resources). Both end the same way — code and state brought back into agreement, proven by a plan that shows no changes.
Tier: Core · Workflow area: Infrastructure as Code
AI accelerates the tedious middle: narrating what drifted and why it matters, planning a safe reconciliation order, and generating import blocks plus matching HCL. The objective gate is beautifully binary: terraform plan says No changes, or you are not done.
Skip AI for single-resource drift you already understand — terraform apply or one import block, done.
aws cli/az/gcloud) for inventory.Capture the evidence. For drift: terraform plan -refresh-only -out=drift.plan then terraform show -json drift.plan > drift.json. For brownfield: inventory the unmanaged resources (aws resourcegroupstaggingapi get-resources, or driftctl's unmanaged report).
Ask for a narrative and a safe ordering — not an apply script.
Attached: refresh-only plan JSON showing drift in our prod stack.
For each drifted resource:
1. Describe the drift in plain English: what changed, in which direction
(console-ahead-of-code or code-ahead-of-console).
2. Classify: HARMLESS (tags, descriptions) / RISKY (security groups, IAM,
scaling settings) / DATA-AFFECTING (anything on stateful resources).
3. Recommend per resource: adopt into code (update HCL to match reality),
revert (apply code over reality), or investigate (conflicting intent).
4. Give a reconciliation order that never leaves a security control
loosened mid-sequence.
Flag anything where reverting could cause an outage (e.g., reverting a
scaling change made during an incident).
Investigate before adopting or reverting. The narrative tells you what; only humans can find out why. A security group opened by hand might be an unauthorized change (revert) or an emergency fix for an incident still in progress (adopt, then fix properly). Check change tickets, CloudTrail, and the incident channel before deciding.
For brownfield: generate import blocks + HCL together.
Generate Terraform import blocks and matching resource HCL for these
unmanaged resources (inventory JSON attached; AWS provider ~> 5.40).
Rules:
- One import block per resource, IDs exactly from the inventory.
- HCL must reflect the resource's ACTUAL current configuration from the
attached describe output — not defaults, not best practice. Matching
reality is the goal; improving it comes later, as a separate diff.
- Group into logical modules matching our repo layout (attached tree).
import {
to = aws_security_group.legacy_app
id = "sg-0a1b2c3d4e5f67890"
}
Run the zero-diff gate. terraform plan after import (or after adopt-edits) must show no changes for the touched resources. Every remaining diff means the generated HCL guessed an attribute instead of reading reality — iterate with the plan output until the diff is empty. This gate is non-negotiable and mechanical; do not accept "close enough."
Only then improve. Once at zero-diff, security/cost improvements to newly-adopted resources go as normal PRs through the full chain (pb-08) — deliberately separated from the import so each plan is interpretable.
Never let an AI assistant run terraform state rm, state mv, or apply during reconciliation. State surgery commands are generate-only: the assistant writes them into a script, a human reads every line, a human runs them — after a state backup (terraform state pull > backup.tfstate).
terraform import one-liners because they are reviewable in a PR.The chain applies with the zero-diff gate as the star:
terraform validate + TFLint.No changes for every touched resource — screenshot/paste it into the PR.Inventory and state contain the client's entire infrastructure map — the most sensitive artifact in this track. Default to running everything inside the client boundary: OSS tools throughout, client's in-tenancy AI endpoint for the narrative/generation steps. If only public assistants are available, the narrative step can run on structurally-redacted plan JSON (resource types and change kinds, IDs hashed), but import-block generation needs real IDs — do that half without AI, or fully in-tenancy.
terraform plan shows zero changes at reconciliation end — the gate passed mechanically, not by judgment call.