terraform state surgery without losing a finger
Contents
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
Sign in to join the conversation.
Sign in to comment