Chapter 4 of 24

The Plan and Apply Lifecycle

A plan is a reviewed decision; apply is a separate, stateful operation that can be interrupted.

Learning objectives

One lifecycle, distinct decisions

Plan file

plan -out=tfplan freezes the reviewed actions and input values for a later apply.

Refresh

Planning refreshes state by default. Use -refresh-only to review observed drift without changing configuration intent.

Replacement

An immutable argument or triggers_replace makes an object destroy/create or create/destroy.

Timeouts and provisioners

Provider timeouts belong where an API is slow. Provisioners are a last resort: they are not idempotent resource management.

lifecycle {
create_before_destroy = true
prevent_destroy = true # remove only for a reviewed deletion
ignore_changes = [tags["external-owner"]]
}

create_before_destroy needs room for two objects. prevent_destroy deliberately blocks a plan. ignore_changes should name one external owner’s field, never hide broad drift.

Apply rule: apply a saved plan only after reviewing its account, workspace, inputs, action counts, replacement reasons, and cost impact.

AWS context

This lab stays local. On AWS, inspect region, account, IAM role, and replacement impact before apply; lifecycle options cannot make an unsafe target safe.

aws sts get-caller-identity --profile learning
aws configure get region --profile learning

Lab: a deliberate replacement $0

Open labs/04-lifecycle/main.tf. The input is ignored to model one externally owned field, while changing release triggers a replacement with create_before_destroy.

Terraform

cd learn-terraform/labs/04-lifecycle
terraform init -backend=false
terraform plan -out=tfplan
terraform show tfplan

OpenTofu

cd learn-terraform/labs/04-lifecycle
tofu init -backend=false
tofu plan -out=tfplan
tofu show tfplan

Only after review, use tofu apply tfplan (or terraform apply tfplan) and then test -var='release=v2'. Do not reuse a saved plan after changing files, variables, or state.

Read action symbols, not just totals

Plan signalDecision
+/-Confirm create-before-destroy is safe for names, quotas, and cost.
-/+Expect downtime unless the API supports another migration path.
must be replacedIdentify exactly which argument caused it.
no changesStill confirm workspace and refreshed identity were intended.

Saved plans contain sensitive planned values; treat tfplan like state and do not commit it.

Failure drill: interrupted apply

If an apply is interrupted, do not rerun blindly. First run terraform plan or tofu plan, inspect recorded and real objects, then repair the smallest discrepancy. A state lock is evidence of a running or failed operation; force-unlock only after proving the owner is gone.

Do not add a shell provisioner to “finish” an interrupted apply: it creates a second untracked control path.

Verify

  1. Inspect a saved plan with terraform show tfplan or tofu show tfplan.
  2. Change only release and confirm replacement is planned.
  3. Change only input and confirm ignore_changes prevents an update.
  4. Remove the plan file after the exercise; it can contain sensitive values.

Cleanup

If you applied, run terraform destroy or tofu destroy with the same CLI. Delete tfplan, terraform.tfstate*, and .terraform after the local exercise.

Quick check

Why save a plan?

It separates reviewing exact proposed actions from applying those same actions later.

Does ignore_changes fix drift?

No. It deliberately stops configuration from managing a narrow externally owned field.

When can force-unlock be safe?

Only after proving no process or CI job still owns the lock.

Recap and next

Plan, review, and apply are distinct checkpoints. Lifecycle rules describe exceptional object behavior and should remain narrow and visible in review.

Next: Chapter 5, State Without Fear.