Forced Replacement and Refactoring

Scenario: A running server becomes corrupted at the OS level after a bad in-place update, but nothing about its Terraform configuration has changed at all, so a completely normal plan proposes no action whatsoever.

Sometimes you need to force a specific resource to be replaced even though its configuration genuinely hasn't changed - exactly the scenario above, where the real object is broken but the desired configuration for it is still perfectly correct. terraform apply -replace=fakecloud_server.api asks Terraform to include a deliberate replacement of that one address as part of an otherwise-normal, reviewable plan - you can see exactly what it will do before it happens, same as any other plan. This is the modern, preferred approach, and it has superseded the older terraform taint command, which instead directly marked an instance for replacement inside the state file itself - a change with no plan-time visibility beforehand, making it a much easier command to trigger by accident or forget you'd run.

It's worth being clear about what -replace is for versus what it explicitly is not for: it is for "this specific object is broken and I want a controlled, reviewed replacement," never for "I renamed this resource block and want to make the mismatch go away" - that second case is a refactor, and the correct tool is a moved block or state mv (Module 5), not forced replacement, because those preserve the object's identity rather than destroying a perfectly healthy one. Finally, terraform_data is a small built-in resource (no external provider needed) purely for holding trigger values - triggers_replace lets you say "replace this whenever this computed value changes," a clean way to model "redeploy when the release checksum changes" without inventing a fake external resource just to hang a trigger on.

Analogy: Forced replacement is swapping out a genuinely broken appliance for a fresh one of the identical model. A rename-driven refactor is just relabeling the circuit breaker for that same, still-perfectly-working appliance so the panel is easier to read. Confusing the two - relabeling by throwing away and rebuying the appliance - is needless demolition for something that was never actually broken.

A worked configuration

terraform plan -replace=fakecloud_server.api -out=recovery.tfplan
terraform apply recovery.tfplan

resource "terraform_data" "release" {
  triggers_replace = [var.release_id]
}

Before running -replace against anything with dependents, inspect what downstream resources reference that address and whether replacing it will cascade into changes or brief unavailability for them too - the dependency graph from Module 1 works the same way here as for any other planned change. In automation, always record why a forced replacement was requested (which incident, which ticket) directly in the change record, and remove any temporary terraform_data triggers once the incident that justified them is over, so the next routine plan doesn't carry forward a trigger nobody remembers the reason for.

Common mistakes in production

Goal: Treat the plan as a change contract: understand every create, update, replacement, and destroy before approving it. Reinforce this lesson in the tf-terraform-data-triggers Terraform lab. Open /labs/terraform and choose slug tf-terraform-data-triggers; the lab runs real Terraform against the offline FakeCloud provider.