Introduction
Terraform identifies infrastructure by resource address, not by the visible cloud object name. If you rename a resource or move it into a new module without telling Terraform how the address changed, the next plan often shows a destroy and create cycle even though the real object should be preserved.
Symptoms
terraform planwants to destroy an existing resource and create a nearly identical replacement- The cloud resource already exists and should have survived the refactor
- The problem started after renaming a resource block or moving it into another module
- The diff shows almost no infrastructure change besides the address
Common Causes
- A resource block was renamed without a
movedblock - A module was restructured and the resource address changed
- State migration commands were skipped during refactor rollout
- The team assumed Terraform would infer renames automatically
Step-by-Step Fix
- 1.Compare the old and new resource addresses
- 2.Before applying anything, confirm that the new address represents the same real-world object.
terraform state list
git diff -- '*.tf'- 1.**Add a
movedblock when the rename can be expressed in code** - 2.For straightforward renames, keep the migration in version control so every environment can replay it safely.
moved {
from = aws_s3_bucket.logs
to = aws_s3_bucket.access_logs
}- 1.**Use
terraform state mvfor one-off migrations** - 2.If a code-based move is not practical, update the state address directly before the next plan.
terraform state mv aws_s3_bucket.logs aws_s3_bucket.access_logs- 1.Re-run plan and verify the change is non-destructive
- 2.A correct migration removes the destroy/create cycle and leaves only intended field changes, if any.
terraform planPrevention
- Treat renames as state migrations, not as ordinary refactors
- Use
movedblocks when the migration should be reproducible across environments - Review destroy/create plans carefully after any module restructure
- Document state move commands in the pull request and rollout notes