Terraform Associate moved blocks for renames and module refactors without replacement
Change the address and Terraform replaces it, unless you say where it went.
Terraform tracks resources by address. Change the address and it plans a destroy and a create, unless you tell it where the old one went.
§I. Frame
State maps each address, such as terraform_data.web, to a real object. Rename the block or move it into a module and the old address disappears from config while the new one has no state. Terraform reads that as "destroy one, create one." For a database or the S3 bucket in the paired Ops lesson, that is an outage.
Brikman covers the two fixes: the imperative terraform state mv, and since Terraform 1.1 the declarative moved block.
§II. Three techniques
Address Is Identity (named technique). Before any refactor, write down the old address and the new one. Both forms count: terraform_data.web to terraform_data.frontend (rename), and terraform_data.cache to module.site.terraform_data.cache (into a module).
Declare the Move (named technique). A moved block lives in config, goes through review, and shows up in plan:
moved {
from = terraform_data.web
to = terraform_data.frontend
}
moved {
from = terraform_data.cache
to = module.site.terraform_data.cache
}
terraform state mv does the same edit directly on state, outside review, one command per address, and every other workspace has to repeat it.
Read the Plan Line (named technique). The plan says has moved to for a clean move and Plan: 0 to add, 0 to change, 0 to destroy. Anything else means the move did not match.
§III. Run it
Start: two resources applied from before/main.tf.
Apply complete! Resources: 2 added, 0 changed, 0 destroyed.
terraform_data.cache
terraform_data.web
Swap in after/ (rename plus a new module "site"), without the moved blocks:
# terraform_data.cache will be destroyed
# terraform_data.frontend will be created
# terraform_data.web will be destroyed
# module.site.terraform_data.cache will be created
Plan: 2 to add, 0 to change, 2 to destroy.
Same config with the two moved blocks (real plan, trimmed):
# terraform_data.web has moved to terraform_data.frontend
# terraform_data.cache has moved to module.site.terraform_data.cache
Plan: 0 to add, 0 to change, 0 to destroy.
Apply, then terraform state list:
terraform_data.frontend
module.site.terraform_data.cache
The ids in the plan (b06e3194-..., cc241cc7-...) are the same objects, now at new addresses.
§IV. Exam edges
movedneeds Terraform 1.1 or later. Module-to-module moves needterraform initfirst so the new module is installed.- In a shared module, keep old moved blocks. The docs treat removing one as a breaking change for callers who have not applied yet.
- A
movedblock does not change real infrastructure. It edits state during apply. terraform state mvis still right for one-off fixes in a single workspace, and it takes effect immediately.
§V. Drills
- You rename
aws_s3_bucket.logstoaws_s3_bucket.audit_logsand plan. What do you expect without a moved block? (One destroy, one create.) - Which approach is visible in code review? (
movedblock.) - The plan shows
has moved toand1 to change. Is the move broken? (No; the move matched and an attribute also changed.) - Where does
frompoint when moving a resource intomodule.site? (The old root address;toismodule.site.<type>.<name>.)
Close
Address Is Identity, Declare the Move, Read the Plan Line. Write the moved block, plan, and look for zero destroys before you apply.
Related
- Tome: Brikman ch5 refactoring, pp.300-301 (grounded-in)
- Web: Refactoring · moved block · state mv · Associate 003 review
- Paired Ops: S3 Block Public Access (2026-10-11)