100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace
HCL

Refactoring State with moved Blocks

The moved block lets Terraform track renamed or relocated resources declaratively, avoiding destructive replace plans when refactoring configuration.

State ManagementAdvanced8 min readJul 9, 2026
Analogies

Refactoring State with moved Blocks

State ties resource addresses (like aws_instance.web) to real-world object IDs. If you rename a resource in configuration, change a module's name, or convert a single resource into a for_each-based collection, the address in configuration no longer matches the address recorded in state — and by default, Terraform interprets that as 'the old resource should be destroyed and a new one created,' even though nothing about the underlying infrastructure actually needs to change. Before Terraform 1.1, the only fix was the imperative terraform state mv command, run by hand outside of version control. The moved block, introduced in 1.1, lets you declare that mapping directly in configuration instead.

🏏

Cricket analogy: If a scorer's records list a bowler under his old team name after a mid-series trade, the system flags him as a brand-new player with zero wickets instead of recognizing his career carries over — the same false 'destroy and recreate' Terraform assumes on a rename.

How moved Blocks Work

A moved block specifies a from address and a to address. When Terraform plans, it checks whether the from address still exists in state; if so, it silently updates the state entry to the to address instead of proposing a destroy/create pair. Because this logic lives in configuration, it travels with the code through version control and gets applied automatically for every consumer of that configuration — including in CI pipelines where no human is present to run an interactive state mv command.

🏏

Cricket analogy: A transfer ledger entry saying 'credit this player's stats from his old team ID to his new team ID' quietly updates the record book the moment the trade is confirmed, rather than wiping his career stats and starting fresh — exactly what a moved block does with state.

Common Refactoring Scenarios

moved blocks handle several everyday refactors: renaming a resource label (aws_instance.web to aws_instance.app_server), moving a resource into or out of a module (aws_instance.web to module.compute.aws_instance.web), and converting a resource from a single instance to count or for_each (aws_instance.web to aws_instance.web[0]). Multiple moved blocks can be chained across successive refactors, and Terraform will follow the chain, though it's good practice to periodically clean up old entries once every consumer has applied past them.

🏏

Cricket analogy: moved blocks cover renaming a player on the scorecard (Kohli to V.Kohli), moving him between squads (Kohli to squad.rcb.Kohli), and splitting one all-rounder's stats into separate batting and bowling entries — chainable across successive seasons of restructuring, but worth pruning once settled.

hcl
moved {
  from = aws_instance.web
  to   = aws_instance.app_server
}

resource "aws_instance" "app_server" {
  ami           = "ami-0abcd1234ef567890"
  instance_type = "t3.medium"
}

# Moving a resource into a module
moved {
  from = aws_security_group.web_sg
  to   = module.networking.aws_security_group.web_sg
}

A moved block works like a forwarding address you file with the post office after moving house: mail (state) addressed to the old location still finds you at the new one, instead of being marked undeliverable and triggering someone to build you an entirely new house.

moved blocks only prevent a destroy/create when the resource's underlying identity truly hasn't changed. If you rename a resource and simultaneously change an immutable argument that forces replacement anyway, Terraform will still show a replace — the moved block only handles the address mapping, not argument-driven replacement.

  • moved blocks declare that a resource address changed without the underlying real-world object changing.
  • Without a moved block, Terraform's default assumption for a changed address is destroy the old, create a new one.
  • moved blocks are checked in configuration during plan, so they work automatically in CI without an interactive command.
  • Common uses: renaming a resource, moving it into or out of a module, or converting it to count/for_each addressing.
  • moved blocks can be chained across multiple historical refactors and Terraform follows the chain.
  • moved blocks only remap addresses — they don't suppress replacement caused by an actual immutable argument change.

Practice what you learned

Was this page helpful?

Topics covered

#HCL#TerraformInfrastructureAsCodeStudyNotes#DevOps#RefactoringStateWithMovedBlocks#Refactoring#State#Moved#Blocks#StudyNotes#SkillVeris