Skip to main content
RunBook Academy

← All labs in Terraform

Lab · advanced · ~25 min

Lab: Refactoring Resources with the moved Block

C · Simulation

Objectives

  • Create a resource with the local provider
  • Refactor the resource address with a moved block
  • Verify the state is preserved without recreation
  • Recognise when moved blocks are the right tool

Prerequisites

Objective

By the end of this lab, you will have:

  • Created a resource with a name that is going to be renamed.
  • Renamed the resource in the configuration.
  • Added a moved block to preserve the state.
  • Verified the plan is empty after the refactor.
  • Verified the real-world file was not recreated.

Requirements

  • A Linux or macOS workstation with shell access.
  • The Terraform CLI 1.9.x or later installed.

Scenario

You have a configuration that creates a file with a name that is no longer appropriate. The refactor renames the resource without recreating the file. The moved block is the production control.

Tasks

Task 1: Create the working directory

mkdir -p ~/rb-moved-lab
cd ~/rb-moved-lab

Task 2: Initial configuration

Create main.tf:

terraform {
  required_version = ">= 1.9.0"
  required_providers {
    local = {
      source  = "hashicorp/local"
      version = "~> 2.5"
    }
  }
}

resource "local_file" "greeting" {
  filename = "${path.module}/hello.txt"
  content  = "Hello from Terraform!\n"
}

Task 3: Initialise and apply

terraform init
terraform apply

Verify the file:

cat ~/rb-moved-lab/hello.txt

Task 4: Inspect the state

terraform state list
terraform state show local_file.greeting

The state has the local_file.greeting resource.

Task 5: Refactor the resource name

Edit main.tf to rename the resource:

resource "local_file" "app_greeting" {
  filename = "${path.module}/hello.txt"
  content  = "Hello from Terraform!\n"
}

Task 6: Plan without a moved block

terraform plan

The plan shows:

# local_file.greeting will be destroyed
+ resource "local_file" "app_greeting" {
    # ...
  }

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

The plan proposes to destroy the old resource and create the new one. Do not apply. This is the production antipattern.

Task 7: Add the moved block

Edit main.tf to add the moved block:

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

resource "local_file" "app_greeting" {
  filename = "${path.module}/hello.txt"
  content  = "Hello from Terraform!\n"
}

Task 8: Plan with the moved block

terraform plan

The plan is now:

No changes. Your infrastructure matches the configuration.

The state has been updated to the new address. The real-world file is unchanged.

Task 9: Apply the refactor

terraform apply

The apply is a no-op. The state is updated; the real world is unchanged.

Task 10: Verify the state

terraform state list

Expected:

local_file.app_greeting

The state has the new address.

cat ~/rb-moved-lab/hello.txt

The file content is unchanged.

Task 11: Verify the file was not recreated

ls -la ~/rb-moved-lab/hello.txt

The files modification time is unchanged. The file was not recreated.

Task 12: Remove the moved block

The moved block is one-shot. After the refactor, it can be removed:

# Removed the moved block

resource "local_file" "app_greeting" {
  filename = "${path.module}/hello.txt"
  content  = "Hello from Terraform!\n"
}
terraform plan

The plan is still empty.

Validation

The lab is successful if:

  • The resource was renamed in the configuration.
  • The moved block was added.
  • The plan is empty after the refactor.
  • The real-world file was not recreated.
  • The state has the new resource address.

Expected Outcome

At the end of the lab:

+---------------------------------+
| ~/rb-moved-lab/                    |
|   .terraform/                    |
|   .terraform.lock.hcl            |
|   hello.txt                       |
|   main.tf                        |
+---------------------------------+

The hello.txt file content is unchanged. The state has the new resource address. The plan is empty.

Cleanup

cd ~/rb-moved-lab
terraform destroy
rm -rf .terraform .terraform.lock.hcl terraform.tfstate*

The main.tf is the only artefact worth keeping.

What You Learned

You learned the moved block pattern:

  1. The refactor is in two parts. The configuration is renamed, and the moved block tells Terraform the new address.
  2. The state is updated, not the real world. The moved block is a state operation.
  3. The plan is empty after the refactor. The refactor is invisible to the apply.
  4. The moved block is one-shot. It can be removed after the refactor.
  5. A wrong moved block is binding. The next plan would show the wrong binding.

Deliverables

  • · A working configuration with a renamed resource
  • · A moved block that preserves the state
  • · An empty plan after the refactor
  • · A real-world file that was not recreated

Verification status

Last reviewed
2026-08-12
Executed end to end
not yet run on hardware

The commands and configuration here have been reviewed against the verified software versions, but nobody has run this lab start to finish on a system meeting its prerequisites. Treat the Expected Outcome as the intended result rather than an observed one, and keep the Cleanup section to hand.