Skip to content
terraform state surgery without losing a finger
article3 min read178 viewsSep 27, 2026

terraform state surgery without losing a finger

Marcus Johnson@marcus_oncall

September 27, 2026 · 3 min read

#1 in YAML Hell

178 views3 comments

Every terraform codebase eventually needs refactoring. You rename a resource, move stuff into a module, split one giant root into three. And the first time you do it terraform helpfully offers to DESTROY your production database and create a new one at the new address. Fun.

Here's the order I do things in now. Mostly learned by almost doing it wrong.

0. back up the state. every time.

terraform state pull > backup-$(date +%Y%m%d-%H%M).tfstate

Your remote backend probably has versioning (turn it on for the S3 bucket if you havent, seriously, go now) but a local copy right before surgery costs nothing.

1. prefer moved blocks over state mv

Since Terraform 1.1 you can declare the rename in code:

moved {
  from = aws_db_instance.main
  to   = module.database.aws_db_instance.this
}

This is so much better than running terraform state mv by hand because it goes through code review, it works for everyone elses workspace too, and the plan shows you exactly what is happening. Leave the moved block in for a while so other environments catch up, clean it up later.

2. import blocks for things created by hand

Someone clicked a security group into existence in the console during an incident (it was me). Since 1.5 you can use an import block instead of the old CLI command, and terraform plan -generate-config-out=generated.tf will even write a first draft of the resource config for you. The generated code is ugly, clean it up, but its a great start.

3. the plan must say no changes

For a pure refactor the goal is: plan shows moves, and zero creates, destroys or updates. If you see a single destroy on something stateful, STOP. Dont "just apply and see". I put prevent_destroy = true in a lifecycle block on databases and buckets exactly so this mistake fails loudly instead of quietly.

4. state rm is for leaving, not for fixing

terraform state rm makes terraform forget a resource without deleting it. Useful when you hand something over to another stack. Terrible when used to "make the error go away", because now you have a real resource nobody manages and it will drift forever. (Since 1.7 there's also a removed block for doing this in code.)

5. never edit the state json by hand

I know you can. I know it's just JSON. I watched someone fix a serial number by hand and break the lineage and we spent an evening restoring from the backup in step 0. Which is why step 0 exists.

Also make sure state locking actually works before any of this (DynamoDB table for the S3 backend, or whatever your backend uses). Two people doing surgery on the same state at the same time is how you lose more than a finger.

Written by

Marcus Johnson

SRE at a freight logistics company in Atlanta. Kubernetes, Terraform, pagers. I have been woken up by every possible alert. Smoker (the BBQ kind).

Get the best of #yaml-hell weekly

One email each Monday with the most useful posts. No spam; unsubscribe in one click.

Comments

3

Sign in to join the conversation.

Sign in to comment
Fatima Al-Sayed@fatima_appsec#2 in YAML Hell
prevent_destroy on stateful resources should be the default in every module template. Also restrict who can run apply against prod from a laptop. Ours only runs from the pipeline with a short lived role.
0 net votes
Ben Carter@bencarter_web#2 in YAML Hell
i read "terraform state surgery" and thought this was going to be about gardening. still learned something
1 net votes
Marcus Johnson@marcus_oncall#1 in YAML Hell
terraforming your garden is a whole other on-call rotation ben
0 net votes