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.

More terraform tools

Terraform Validator & Formatter Does it parse, and is it formatted? Terraform dynamic Block Generator It is ingress.value, not each.value Terraform check and precondition Generator A failed check does not stop the apply Terraform variables.tf Generator From the var. references you already wrote Terraform Unused Declaration Linter Dead variables, locals and data sources Terraform Atlantis YAML Generator when_modified misses your modules Terraform Function Tester and HCL Console terraform console, in a tab Terraform for Expression Tester Where do the keys collide? Terraform Interpolation Tester null becomes an empty string Terraform templatefile Tester Render it before you apply it Terraform Resource Count Calculator How many objects, not how many blocks Terraform count to for_each Converter With the moved blocks it needs Terraform moved Block Generator Rename without destroying Terraform target Flag Builder And why to stop using it Terraform outputs.tf Generator Outputs are not sensitive by default Terraform Remote State Data Source Generator It grants read of the whole state Terraform Provider Block Generator docker is not hashicorp/docker Terraform .gitignore Generator and Checker .terraform* eats the lock file Terraform to OpenTofu Migration Checker The state is the part to watch Terraform tfvars to JSON Converter The JSON file wins Terraform Version Constraint Parser What ~> actually allows Terraform Resource Address Parser Quote it before the shell eats it Terraform cidrsubnet Calculator newbits is added, not the target Terraform Import Block Generator The id is not the name Terraform Lock File Viewer Why CI fails and your laptop does not Terraform Variables Checker A missing value hangs CI Terraform tfvars to Env Vars Converter Lists and maps have to be JSON Terraform Deprecated Syntax Checker What a current Terraform rejects Terraform State File Viewer Read it without uploading it Terraform Plan JSON Viewer It is not redacted Terraform HCL to JSON Converter And what the conversion loses Terraform Module Source Validator version only works for the registry Terraform Backend Config Decoder Is state actually locked? Terraform Naming Convention Validator A hyphen makes a resource unreferenceable

Elsewhere on the site