Q: How do you migrate Terraform state without recreating resources?
Step-by-step runbook to migrate terraform state without recreating live resources: refactoring into modules, state surgical moves, declarative moved blocks (Terraform 1.1+), and backend migration.
Want to master this scenario in a live sandbox? KodeKloud's HashiCorp Certified Terraform Associate (003) Interactive Labs covers this exact problem with hands-on terminal drills.
🛠️ Production Runbook & Step-by-Step Resolution
Direct Answer: When refactoring Terraform code (e.g., modularizing resources or changing resource names), Terraform by default interprets the old resource as deleted and the new resource as new, planning a disastrous 'destroy and re-create'. In an interview, I explain how to migrate terraform state without recreating live production infrastructure using declarative 'moved' blocks and 'terraform state mv'.
The Refactoring Trap: Destroy & Re-create Disaster
If you take an existing aws_security_group.web and move it inside module.security.aws_security_group.web, running terraform plan will output: Plan: 1 to add, 0 to change, 1 to destroy. Applying this destroys live security groups, taking down production traffic.
- Root Cause: Terraform binds real-world cloud resource IDs to the specific HCL address path stored in the state file.
- Goal: Update the state file address mapping to match the new HCL code path without issuing cloud API delete calls.
Modern Approach: Declarative 'moved' Blocks (Terraform 1.1+)
Terraform 1.1 introduced declarative moved blocks. Instead of running brittle manual CLI commands, engineers declare resource migrations directly in HCL code, which can be peer-reviewed in pull requests.
# Refactoring a standalone resource into a module
moved {
from = aws_security_group.web
to = module.security.aws_security_group.web
}
# Refactoring a count-based resource to for_each
moved {
from = aws_subnet.public[0]
to = aws_subnet.public["us-east-1a"]
}
- GitOps Safe: All team members and CI pipelines pick up the migration automatically without manual CLI intervention.
- Plan Output:
terraform planshows:Resource has moved: aws_security_group.web -> module.security.aws_security_group.webwith 0 to add, 0 to destroy.
CLI Approach: 'terraform state mv' for Precision Surgery
For ad-hoc refactoring or migrating resources between modules when moved blocks are not applicable, use terraform state mv.
# Step 1: Always pull a local backup of remote state first
terraform state pull > state-backup-$(date +%F).json
# Step 2: Move the resource address in the state file
terraform state mv aws_instance.web module.compute.aws_instance.web
# Step 3: Verify the plan shows ZERO changes
terraform plan
- Zero Infrastructure Impact: Modifies only the internal JSON state pointers; no AWS or cloud API calls are made.
- Plan Verification:
terraform planmust showNo changes. Your infrastructure matches the configuration.
Cross-State Migration: Splitting Monolithic States
When splitting a massive monolithic state file into separate micro-states (e.g. isolating Networking from Compute), resources must be moved across distinct state backends.
# In monolithic state directory: remove resource from state WITHOUT deleting cloud resource
terraform state rm aws_vpc.main
# In new dedicated networking state directory: import existing cloud resource ID
terraform import aws_vpc.main vpc-0123456789abcdef0
# Verify clean plan
terraform plan
- state rm: Deletes the resource tracking from the old state file without touching the live AWS resource.
- import: Adopts the live resource into the new state file.
Migrate Terraform State Without Recreating: Decision Matrix
Evaluating techniques to migrate terraform state without recreating resources:
- The Problem: Renaming resources or moving them into modules causes Terraform to plan a destructive destroy-and-recreate.
- Declarative 'moved' Blocks: Use HCL moved { from = ... to = ... } blocks (Terraform 1.1+); it is version-controlled and executes seamlessly in CI.
- CLI Surgery: For manual refactoring, take a local state backup (terraform state pull) and run 'terraform state mv old_addr new_addr'.
- Plan Validation: Verify that terraform plan outputs 'Resource has moved' and confirms 0 to add, 0 to destroy.
- Cross-State Migration: When decoupling monolithic states, use 'terraform state rm' in the source and 'terraform import' in the destination.
Senior hiring managers and bar raisers evaluate how you prioritize during outages. Avoid these common anti-patterns:
- Applying code refactoring before updating the state mapping Applying without moved blocks or state mv will permanently destroy the existing live resource and attempt to recreate a new one.
- Failing to backup the remote state file prior to performing state surgery A typo in a state mv command can corrupt the state file; always take a snapshot with 'terraform state pull > backup.json'.
- Forgetting quotes around indexed resources in bash shells Running terraform state mv aws_subnet.pub[0] without quotes causes bash to interpret brackets as file globbing, resulting in syntax errors.
- Removing resources using 'terraform destroy' instead of 'terraform state rm' terraform destroy terminates the real live production infrastructure; terraform state rm only removes the tracking pointer from state.