5.6. Moved Block

Preparation

Create a new directory for this exercise:

mkdir -p $LAB_ROOT/advanced/moved
cd $LAB_ROOT/advanced/moved

Optional: Create empty files:

touch {main,versions}.tf

Step 5.6.1: Initial setup

Create a new file named main.tf with an intentionally short resource name that we will later want to rename:

resource "local_file" "tmp" {
  filename = "output.txt"
  content  = "hello from terraform"
}

Create a new file named versions.tf and add the following content:

terraform {
  required_version = ">= 1.12.2"

  required_providers {
    local = {
      source  = "hashicorp/local"
      version = "= 2.5.2"
    }
  }
}

Apply to create the resource in state:

terraform init
terraform apply

Step 5.6.2: Rename the resource – the wrong way

Rename local_file.tmp to local_file.greeting in main.tf without a moved block:

resource "local_file" "greeting" {
  filename = "output.txt"
  content  = "hello from terraform"
}

Run terraform plan:

terraform plan
Plan: 1 to add, 0 to change, 1 to destroy.

Terraform treats this as a delete + create because the old address no longer exists. In a real environment this would delete and re-provision a running resource—potentially causing downtime. Do not apply. Restore the original name before continuing.

Step 5.6.3: Rename safely with a moved block

Update main.tf to the new name and add a moved block that tells Terraform the old and new addresses:

resource "local_file" "greeting" {
  filename = "output.txt"
  content  = "hello from terraform"
}

moved {
  from = local_file.tmp
  to   = local_file.greeting
}

Run terraform plan:

terraform plan
Terraform will perform the following actions:

  # local_file.tmp has moved to local_file.greeting
    resource "local_file" "greeting" {
        ...
    }

Plan: 0 to add, 0 to change, 0 to destroy.

No destroy/create cycle—Terraform simply updates the state record. Apply to confirm:

terraform apply

After the apply succeeds you can remove the moved block (or keep it as documentation of the rename history).

Explanation

The moved block (introduced in Terraform 1.1) records a renaming or refactoring operation directly in the code. Its key properties:

  • Zero-downtime renames – no destroy/create occurs; only the state entry is relocated.
  • Module moves – can relocate resources across module boundaries:
    moved {
      from = local_file.greeting
      to   = module.files.local_file.greeting
    }
    
  • Idempotent – if the from address is not in state (e.g. already moved), the block is a no-op.
  • Reviewable – the rename is explicit in code, visible in pull requests.

Step 5.6.4: Rename within a for_each resource

The moved block also supports renaming keys within a for_each resource. For example, to rename the key "dev" to "development" without re-creating the resource:

moved {
  from = local_file.configs["dev"]
  to   = local_file.configs["development"]
}

This pattern is useful when cleaning up naming conventions in an existing for_each map.