Terraform Fundamentals · Module 08 of 12

Lifecycle, refactoring, moved/removed blocks and import

Refactor Terraform addresses without unnecessary replacement, understand lifecycle controls, and bring existing infrastructure under management carefully.

Learning objectives

By the end of this module, you should be able to:

  • Use moved blocks to preserve object identity during refactors.
  • Explain when lifecycle settings help and when they hide design problems.
  • Describe configuration-driven import and its provider-specific constraints.

Refactoring must preserve state identity

Renaming a resource in configuration normally looks like destroy-and-create because the address changed. A moved block records the address transition so Terraform can preserve the existing managed object.

Lifecycle changes resource behavior

create_before_destroy, prevent_destroy, ignore_changes and replace_triggered_by affect lifecycle decisions. They should be used intentionally because they can change risk and drift behavior.

Import adopts existing objects

Import maps an existing provider object to a Terraform resource address. Configuration-driven import blocks make imports reviewable as part of plan/apply, but the provider resource must support import and configuration must match reality.

removed blocks support explicit handoff

A removed block can remove a resource or module from Terraform state through the normal plan/apply workflow. Setting lifecycle { destroy = false } tells Terraform to stop managing the object without destroying the underlying infrastructure, which is safer and more reviewable than an ad-hoc state rm for planned ownership handoffs.

Worked examples

See the idea in practice.

Address-preserving refactor

moved {
  from = terraform_data.old_name
  to   = terraform_data.service
}

resource "terraform_data" "service" { input = "api" }
  • Terraform can update state address ownership instead of replacing the underlying object.

Configuration-driven import pattern

import {
  to = aws_s3_bucket.logs
  id = "existing-logs-bucket"
}
  • The exact import identifier and support depend on the provider resource.

Stop managing without destroying

removed {
  from = aws_instance.legacy
  lifecycle {
    destroy = false
  }
}
  • The plan can show that Terraform will forget the object while leaving the remote infrastructure intact.
  • Remove downstream references before applying the handoff.
Hands-on lab

Lab — Refactor an address with a moved block

Goal: Rename a managed resource without creating or destroying it.

Before you start

  • Terraform CLI only.
STEP 1

Create initial object

mkdir -p ~/gnu-terraform-labs/module08 && cd ~/gnu-terraform-labs/module08
cat > main.tf <<'EOF'
resource "terraform_data" "old_name" {
  input = "api"
}
EOF
terraform init
terraform apply -auto-approve
STEP 2

Refactor with moved block

cat > main.tf <<'EOF'
moved {
  from = terraform_data.old_name
  to   = terraform_data.service
}

resource "terraform_data" "service" {
  input = "api"
}
EOF
STEP 3

Review plan

terraform plan
STEP 4

Apply and inspect state

terraform apply -auto-approve
terraform state list

Verify

  • The plan reports an address move rather than a destroy/create replacement.
  • State contains terraform_data.service and not terraform_data.old_name.

Expected outcome

  • You can refactor Terraform addresses while preserving state identity.

If it fails

  • A moved destination must match a compatible resource/module address.
  • Import behavior is provider-specific; read the resource import documentation before adopting existing objects.
Real-world connection

Address migrations should be reviewed like schema migrations because many downstream tools and state references depend on stable addresses.

Avoid these traps

Common mistakes

  • Renaming resources without checking the plan.
  • Using ignore_changes to hide all drift indefinitely.
  • Importing the same remote object into multiple Terraform addresses.
Knowledge check

Can you explain it without looking back?

Open each answer only after you have tried to answer the question yourself.

1What problem does a moved block solve?

It records an address refactor so Terraform can preserve managed object identity.

2Does every provider resource support import?

No; import support is provider/resource specific.

3Why can ignore_changes be risky?

It can intentionally stop Terraform from reconciling selected drift, masking changes you may care about.

My Learning

Record your progress for this module.

Progress is saved in this browser immediately. When you sign in to My Learning, it can also sync across devices.

Not started

Attempt the core lab and knowledge checks before marking the module complete.