Skip to main content
RunBook Academy

Git, CI/CD & GitOpsLIX · RollbackTerraform

Terraform rollback via state and applied — terraform state list and the targeted apply

Advanced⏱ ~25 mingit

What you'll learn

  • Inspect Terraform state with terraform state list to identify what Terraform owns
  • Perform a rollback by reverting the configuration and applying a fresh reviewed plan, reserving terraform apply -target=<address> for exceptional error recovery
  • Recognise the boundaries of state-driven rollback - resources deleted out-of-band cannot be undone
  • Build the discipline of validating state after a rollback

Prerequisites

Verified against Git 2.55.x teaching target; 2.40+ minimum · GitHub Actions continuous service; Aug 2026 documentation baseline · Argo CD v3.5.x teaching target; v3.0+ minimum · Flux v2.9.x · Sigstore Cosign v3.1.x · SLSA v1.2 · OCI Distribution Specification v1.1 · Git LFS v3.7.1 · Kubernetes (cross-course target) 1.36.x

Not yet marked complete on this device.

Terraform rollback is not a button. It is a state-driven apply. The state file is the inventory of what Terraform owns; the configuration is the declaration of what Terraform wants; the apply reconciles the two. A rollback is the act of pointing the configuration at a previous commit and letting Terraform reconcile. The mechanism is the same apply the team has been running all along; the rollback is a change in the commit the apply reads.

How state-driven rollback works

Terraform’s model is declarative. The configuration declares the desired state; the state file records the current state; the apply reconciles the two. A rollback does not change this model: it changes what the configuration declares.

flowchart LR
    A["Commit HEAD"] --> C["Configuration"]
    B["Commit HEAD~1"] --> C2["Configuration (previous)"]
    C --> D["State file"]
    D --> E["apply (HEAD)"]
    C2 --> D
    D --> F["apply (HEAD~1) = rollback"]

The rollback procedure:

  1. Identify the previous commit. git log on the configuration repository; the post-mortem may name a specific SHA.
  2. Inspect state: terraform state list names every resource Terraform still believes it owns. The list is the inventory for the rollback.
  3. Revert the commit (git revert or new commit that reverts the bad change), run terraform plan against the current state, review the full diff, then apply. The apply reconciles the rolled-back configuration against the current state; the resources Terraform owns are brought back to the previous shape.
  4. Validate. terraform plan after the rollback should show no changes; drift between the rolled-back configuration and the actual cloud is the failure mode.

When the bad change touched a single resource, the revert itself is already scoped: a git revert of the offending commit changes only that resource’s configuration, so the fresh plan touches only that resource. In exceptional error-recovery situations - a partially failed apply that must be unwound before a clean plan can run - Terraform can scope an apply to one address:

terraform apply -target=$ADDRESS

The -target flag scopes the apply to a single resource address (module.x.aws_security_group.y, aws_instance.z, etc.). Terraform prints a warning every time the flag is used, and the documentation reserves it for error recovery: routine use is an anti-pattern, because a targeted apply skips the rest of the dependency graph and leaves state only partially reconciled.

terraform state list as the inventory

The first command in any rollback is the inventory:

terraform state list

The output is a list of resource addresses in the state. A module is named by its path (module.network.aws_vpc.main); a top-level resource by its address (aws_security_group.api). The list is what Terraform can roll back; everything else has been deleted, replaced, or never tracked.

What the list tells the operator:

  • A resource is missing from the list. It was removed from the configuration and the removal was applied; the rollback can re-declare it, but the cloud resource may have been destroyed. terraform import may be needed.
  • A resource is in the list but not in the cloud. It was deleted out-of-band; the state is stale. The rollback cannot restore what the cloud no longer holds without a recreation.
  • A resource is in the list and in the cloud but drifted. It was modified by hand; the rollback will reconcile it back to the configuration’s declaration, potentially destroying manual changes.

The list is not optional. A rollback that proceeds without inspecting it is gambling that state matches reality.

What state-driven rollback can and cannot do

The boundary state-driven rollback crosses is the cloud resource Terraform owns. What it can do:

  • Revert a resource’s configuration to the shape declared in the previous commit.
  • Recreate a resource that was deleted from the configuration but is still in state and still in the cloud (or importable from the cloud).
  • Destroy a resource that was added in a bad commit and apply against the reverted configuration.

What it cannot do:

  • Restore a resource that was deleted out-of-band (by hand, by another tool, by cloud console). State says Terraform owns it; the cloud does not. The rollback cannot conjure a deleted resource back without terraform import and a recreation.
  • Roll back a non-Terraform change. A resource created by the cloud console, by an Ansible run, by a kubectl apply is invisible to Terraform. The rollback leaves it alone.
  • Roll back data without a backup. A database that Terraform provisioned but a separate process populated is restored to the schema, not to the data. The rollback is structural, not data-level.
  • Roll back a multi-resource change atomically. A Terraform apply is the unit of atomicity. A -target apply on resource A followed by another -target apply on resource B is two applies, not one. The cloud sees intermediate states.

When to use targeted apply

A targeted apply (terraform apply -target=$ADDRESS) is an error-recovery instrument, not a rollback path. Terraform warns on every use; the exceptional cases:

  • A partially failed apply blocks a clean plan. An apply died mid-run and left one resource in a state that makes every full plan error out; the targeted apply unwinds that one resource so the full reviewed plan can run again.
  • A single resource must be repaired before the full reconciliation. A broken dependency prevents the full apply from converging; repairing the one address first unblocks the rest.
  • A dependency deadlock is being unwound step by step. Resource B depends on Resource A; the full apply fails because A still references the new shape. Targeted applies in dependency order unblock each step - followed by a full plan to reconcile.

The risks:

  • Drift introduced by the targeted apply on the rest of the state. A targeted apply changes one resource; the next full plan will show the change correctly, but intermediate states may have inconsistent references.
  • Hidden dependencies. A resource that does not declare a dependency on the targeted resource may nonetheless break when the target is rolled back. The targeted apply does not enforce the full dependency graph.

Production discipline

  1. Run terraform plan before every rollback. Read the diff. Confirm the plan is the expected rollback and not a surprise.
  2. Inspect state with terraform state list before applying. Confirm what Terraform still owns; identify the gap between state and reality.
  3. Prefer the full revert-and-apply over a targeted apply. Terraform warns on every -target use; the flag is for exceptional error recovery, and the full reviewed plan is the default and the safer pattern.
  4. Treat state drift as a rollback blocker, not an inconvenience. A terraform refresh (or plan -refresh-only) before the rollback reconciles state to reality; the rollback then proceeds against an accurate state.
  5. Validate after the rollback. terraform plan should show no changes; a non-empty diff means the rollback is incomplete or the configuration has drifted further.

Cross-course references

  • Terraform for Production Sysadmins - Part XII (State) covers state backends, locking, and drift detection.
  • This course, Part L (Terraform CI) covers the plan-as-artifact discipline that produces auditable rollback targets.
  • This course, Part XXVII (Repository layouts) covers the Terraform repository structure that the rollback reverts.

Quiz

Knowledge check · 4 questions

  1. Q1. A team needs to roll back a Terraform change that affected a single security group, without disturbing the rest of the infrastructure. What is the correct rollback procedure?

  2. Q2. If a resource has been deleted from the cloud by an out-of-band process (a console action, a separate script), Terraform can still roll it back via `terraform apply` against the previous commit because the state file still records Terraform as the owner.

  3. Q3. What is the role of `terraform state list` in a rollback procedure, and what does the operator learn from it?

  4. Q4. Diagnose why a Terraform rollback reported success but the cloud resource did not change, and identify the production discipline that prevents recurrence.

    A team reverts a Terraform commit that opened a security group rule, runs `terraform apply`, and observes 'no changes'. The team believes the rollback succeeded. An hour later, a security audit finds the security group rule is still open. Investigation reveals that a separate on-call engineer had manually edited the security group in the cloud console two weeks earlier to add a temporary rule for debugging. The state file was not refreshed; the apply compared configuration against stale state and produced a no-op.

Passing score: 75%. Answers are checked in this browser.