Using AI to Plan a Safe Terraform State Migration
State surgery is the scariest part of Terraform. AI can map out a state migration plan step by step, but it must never run a single state command itself.
- #terraform
- #ai
- #state
- #migration
Nothing in Terraform makes my palms sweat like a state migration. Moving resources between modules, splitting one giant state file into three, renaming dozens of addresses after a refactor — get it wrong and you either orphan real cloud resources or schedule them for deletion. The plan output looks calm right up until the line aws_db_instance.main will be destroyed.
This is a place where AI earns its keep, but with a hard boundary: the AI plans the migration; a human runs it. The model never touches your state. It doesn’t get the backend credentials, it doesn’t run terraform state mv, and it never sees your real .tfstate. It works from addresses and a plan you paste in. Think of it as a fast junior engineer drafting a runbook that you, the senior, execute by hand.
Why state migrations go wrong
The danger is always the same: the resource address in your state no longer matches the address in your config. When that happens, Terraform sees the old address as “gone” (delete) and the new address as “new” (create). For a stateless resource that’s harmless. For an RDS instance, it’s a Tuesday-afternoon incident.
# aws_db_instance.main will be destroyed
- resource "aws_db_instance" "main" {
# module.database.aws_db_instance.this will be created
+ resource "aws_db_instance" "this" {
That destroy/create pair is the bug. The fix is to tell Terraform “these are the same resource” with moved blocks or terraform state mv. The hard part isn’t the command — it’s working out the complete, correct mapping across a refactor that touched 60 addresses. That’s the work AI is good at.
Feed it the addresses, not the state
Generate the list of current addresses safely:
terraform state list > current-addresses.txt
terraform state list prints addresses only — no secrets, no resource attributes, no provider config. It’s safe to share. Paste that, plus a description of the target structure, and ask:
Here are my current Terraform resource addresses. I’m splitting this into three modules:
network,compute,data. Produce a table mapping each current address to its new address. Flag any resource where the type changes (those can’t bemoved, only re-imported).
The model returns a mapping table. Your job is to audit it: did it route the security groups into network or compute? Did it catch that a resource moving across providers needs import, not move? You’re reviewing a junior’s spreadsheet, line by line.
Prefer moved blocks over state mv
The modern, reviewable way to do this is moved blocks in config, because they go through plan and live in version control:
moved {
from = aws_instance.web
to = module.compute.aws_instance.web
}
moved {
from = aws_db_instance.main
to = module.data.aws_db_instance.this
}
Ask the AI to convert its mapping table into moved blocks. This is mechanical translation — exactly what models do well. Then the critical step that is yours alone:
terraform plan
A correct migration shows no changes — just moves:
Terraform will perform the following actions:
# ... resources have moved, no creation/destruction ...
Plan: 0 to add, 0 to change, 0 to destroy.
0 to destroy is your green light. Anything else means the mapping is wrong, and you go back to the AI with the offending plan lines. The plan is the oracle, not the model’s confidence.
Pro Tip: Run terraform plan -out=tfplan and keep the file. If the human-run migration goes sideways, you have a recorded artifact of exactly what Terraform intended to do at the moment you approved it.
When you genuinely need state mv
Some moves can’t be expressed as moved blocks — for example, pulling a single resource out of count into a named instance, or moving across separate state files. Here the AI’s role is to generate the exact commands as a script you review and run, never to run them:
# REVIEW EACH LINE before running. Back up state first.
terraform state mv 'aws_instance.web[0]' 'aws_instance.web_primary'
terraform state mv 'aws_instance.web[1]' 'aws_instance.web_secondary'
Notice the indices and quoting — that’s the kind of fiddly detail AI gets right and humans fat-finger. But you back up first, every time:
terraform state pull > state-backup-$(date +%s).json
The cross-config move: import, not move
If a resource is moving to a configuration that doesn’t have it in state at all, moved won’t help — you remove it from the source and import it into the target. AI is useful for generating the import blocks at scale:
import {
to = module.data.aws_db_instance.this
id = "main-prod-db"
}
removed {
from = aws_db_instance.main
lifecycle { destroy = false }
}
The lifecycle { destroy = false } on removed is the safety pin: it drops the resource from this state without deleting the real infrastructure. Ask the AI to explain that line back to you before you trust it — if it can’t, don’t run it.
Keep the credentials away from the model
I’ll say it plainly because it’s the whole game: the AI plans, you execute. It never gets your backend credentials, never gets cloud write access, never runs a state command. Everything it produces — moved blocks, import blocks, state mv scripts — passes through terraform plan and a human’s eyes before it touches reality. A migration runbook from a fast junior engineer is a gift. A junior engineer with your state-write keys is an outage waiting to happen.
If you want a vetted set of prompts for this exact workflow, the prompts library and prompt packs have state-migration templates. And before you run anything, AI for Terraform collects the rest of our state and refactor guides.
Conclusion
State migrations are scary because the failure mode is silent until plan reveals a destroy you didn’t expect. AI shrinks the tedious mapping work and writes flawless moved/import blocks — but terraform plan showing 0 to destroy is the only signoff that counts, and a human runs every command. Use the model to draft the runbook; keep the keys, the state, and the apply button to yourself.
Download the Free 500-Prompt DevOps AI Toolkit
500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.
- 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
- Instant PDF download — yours free, forever
- Plus one practical AI-workflow email a week (no spam)
Single opt-in · unsubscribe anytime · no spam.