Terraform state mv Command Builder

Build correctly quoted terraform state mv commands, with the backup step first. On Terraform 1.1 or later, read the second finding before running any of them.

Paste below, or drop a file anywhere on this panel

Or drop a file anywhere on this panel. Nothing is uploaded: the analysis runs in this tab.

The answer appears here

Paste on the left and press Build. Nothing leaves this tab.

Examples

Real input you can load into the tool above. Each one shows a different thing going wrong, because that is what the tool is for.

A move with a for_each key

Brackets and quotes both need shell quoting, or Terraform receives a different address

aws_instance.web[0] -> aws_instance.web["primary"]

Two moves at once

They run in sequence against the result of the last one, so ordering matters

aws_instance.a -> aws_instance.b
aws_instance.c -> aws_instance.d

Common mistakes

These are the ones that fail silently. The config is accepted, nothing raises an error, and the consequence arrives later.

  1. Running state mv against a remote backend without a copy

    Terraform writes a local .backup file only for a local backend. With S3 or GCS there is nothing to go back to, and the command has already taken effect.

    Instead:Run terraform state pull > backup.tfstate first. It is the only recovery route.

  2. Reaching for state mv on Terraform 1.1 or later

    It runs once on one machine, against one state, with no plan and no record in version control. Everyone else's state has to be done by hand.

    Instead:Use a moved block unless you are on an older version or moving between two state files.

  3. Treating a list of moves as independent

    They run in order against the result of the previous one, so an address that is both a source and a destination moves twice.

    Instead:Check for that overlap and break it with a temporary name.

This writes to state the moment it runs, with nothing to review

There is no plan, no confirmation and no dry run. That is the difference between this and a moved block, and it is the reason to prefer the block wherever you can.

Back up state yourself, because a remote backend will not

For a local backend Terraform writes a .backup file. For S3, GCS or any remote backend it does not. terraform state pull > backup.tfstate takes a copy you control, and terraform state push backup.tfstate puts it back. That is the whole recovery plan, so it goes first.

moved blocks are better wherever they work

state mv runs once, against one state, on one machine, and leaves nothing in the repository to show it happened. A moved block is reviewed in a pull request, applies in every workspace and every environment, and works in CI where nobody can type a state command. The two cases state mv still wins are a Terraform older than 1.1, and moving an object into a different state file, which moved blocks cannot do.

Commands run in sequence against the result of the last one

There is no batch mode and no transaction. Moving A to B and then B to C moves the original A all the way to C, which is not what a list of independent renames meant. A failure halfway leaves the earlier moves applied and the rest not.

Brackets have to be quoted

In zsh an unquoted address with brackets fails before Terraform starts. In bash the double quotes around a for_each key are stripped, so Terraform receives an address it cannot parse. Single quotes around the whole address handle both, and the commands here are already written that way.

It takes the state lock

Which is correct: it stops two people rewriting state at once. It also means a lock left behind by a cancelled CI job blocks it, and force-unlock is how people get past that, which is how two writers end up happening anyway. Check what is running instead.

Crossing state files needs -state-out

Pull both states locally, move with -state-out pointing at the second, then push both. Every step is manual and none of it is reviewed, which is why splitting a configuration is worth planning rather than improvising.