Course outline

Terraform / Modules and state-aware lifecycle / Lesson 10

Lifecycle and replacement controls

Understand what lifecycle settings can constrain and why they do not provide availability, data recovery, or unconditional deletion protection.

About 4 min readContent reviewed
On this page

Lesson goals

  • Explain replacement triggers, ordering, prevent_destroy, and ignore_changes.
  • Review provider constraints, quotas, uniqueness, and shared ownership.

Replacement semantics come from a resource contract

A provider can treat an argument change as an in-place update, replacement, or unsupported transition. Lifecycle meta-arguments influence supported Terraform behavior around those actions but do not rewrite the remote API’s rules. Before changing lifecycle settings, identify the actual resource identity, data, dependencies, naming constraints, and downtime implications. The same create-before-destroy idea can be straightforward for one disposable object and impossible for another with a unique fixed name or insufficient quota. A lifecycle block is therefore a reviewed design choice, not a universal reliability switch.

Separate changed data from forced replacement intent

terraform_data provides an explicit teaching distinction: input can hold data, while triggers_replace records values whose change requests replacement of that instance. The fixture below uses a synthetic revision string and create_before_destroy ordering. No external service is represented and no transition is executed. A real resource must still support simultaneous old and new instances when that ordering is required. The example does not prove zero downtime, successful startup, or safe transfer of persistent data.

hcl A declared replacement trigger
terraform {
  required_version = ">= 1.16.0, < 1.17.0"
}

variable "revision" {
  type    = string
  default = "release-a"
}

resource "terraform_data" "release" {
  input            = { revision = var.revision }
  triggers_replace = [var.revision]
  lifecycle {
    create_before_destroy = true
  }
}

Changing revision changes triggers_replace for this built-in resource. That is a specific documented contract, not a rule for all provider input fields. The lifecycle setting requests supported ordering and does not perform an application readiness check. No state is created or changed.

Protection only applies within its documented scope

prevent_destroy rejects plans that would destroy the protected instance while the relevant configuration is present. Removing the resource declaration also removes that configuration-based protection, so it is not a substitute for external permissions, backups, or review. ignore_changes can support deliberate shared management of selected attributes, but can also hide important drift. It does not grant another actor ownership safely by itself or prevent every deletion. Document which actor owns each ignored attribute and how changes are observed and reconciled.

Model one explicit trigger rule offline

The Python example compares authored records under a declared teaching rule. It distinguishes changed input from changed replacement trigger. That narrow model cannot infer replacement for arbitrary providers or reproduce Terraform’s full plan logic, dependency graph, and state history. Keep the result tied to this stated contract and verify real actions through the approved workflow. A tidy rule name such as update should never be interpreted as proof that a real operation is reversible or has no service impact.

python Check a fixed lifecycle distinction
def action(old, new):
    if old['trigger'] != new['trigger']:
        return 'replace'
    return 'update' if old['input'] != new['input'] else 'no-op'
old = {'input': 'blue', 'trigger': 'v1'}
print('input_change=' + action(old, {'input': 'green', 'trigger': 'v1'}))
print('trigger_change=' + action(old, {'input': 'blue', 'trigger': 'v2'}))
Expected output
input_change=update
trigger_change=replace

The declared rule returns update for input-only change and replace for trigger change. No provider, plan, object, or state is inspected. Actual availability and data consequences remain outside this bounded comparison.

Lifecycle review includes dependencies and failure recovery

Ordering changes can propagate through dependencies and affect the complete transition, not merely one isolated block. Check quotas, uniqueness, traffic handoff, readiness, data migration, and how a partial failure will be reconciled. Avoid broad ignore_changes=all as a shortcut to a quiet plan unless the resulting ownership model is intentionally designed and reviewed. Preserve state and logs when a replacement fails. Neither reverting source text nor disabling protection automatically restores a previous real-world state or makes an interrupted transition safe to repeat.

Common errors and fixes

Instance cannot be destroyed.

Why it happens
A proposed action conflicts with a configured prevent_destroy requirement.
How to fix it
Review the actual intended transition and authorization instead of removing protection blindly.
Prevent it next time
Treat protection exceptions as explicit reviewed lifecycle decisions.

create_before_destroy fails under a unique name or quota.

Why it happens
The target system cannot host the required old and new objects simultaneously.
How to fix it
Choose a supported naming, capacity, and handoff strategy for that resource.
Prevent it next time
Rehearse replacement constraints before approving availability claims.

Important drift disappears from review.

Why it happens
ignore_changes was applied beyond a clearly assigned shared-ownership scope.
How to fix it
Narrow ignored attributes and define observation and reconciliation responsibilities.
Prevent it next time
Review ignored fields and owners as an ongoing contract.

Check your understanding

1. Does create_before_destroy guarantee an uninterrupted application?

Show worked solution

No. It concerns supported resource ordering. Application readiness, traffic routing, name uniqueness, quota, dependency behavior, and data handoff are separate requirements. Validate the complete transition and failure path rather than translating a lifecycle flag into an availability promise.

2. Why is deleting a protected resource block not a safe backup strategy?

Show worked solution

The configuration-based prevent_destroy setting disappears with the declaration, while a backup requires retained recoverable data and a tested restore. They protect different boundaries. Preserve ownership evidence and follow the approved lifecycle and recovery process rather than relying on the former flag after it has been removed.

Key takeaways

  • Lifecycle settings constrain particular transitions; they do not replace resource semantics.
  • Protect identity, data, availability, and recovery as separate requirements.

Official references