create_before_destroy and prevent_destroy
Scenario: A broad cleanup plan meant to remove some unused test resources also happens to include the production data store, because it matched the same tag filter used to identify what to clean up - and an engineer only notices after the plan was already approved.
The lifecycle block lets you attach special meta-behavior to a specific resource, on top of its normal arguments. You already met create_before_destroy in the previous section - it reverses the default destroy-then-create ordering for that one resource whenever a replacement is required. prevent_destroy = true is a different, narrower safeguard: while this setting remains present in the configuration, Terraform will flatly refuse to run any plan that would destroy that specific resource instance - including as a side effect of some broader change, exactly the kind of accident in the scenario above.
It's important to understand precisely what kind of protection this is, and isn't. prevent_destroy is a tripwire inside the Terraform apply path specifically - it does nothing to stop someone from deleting the object directly through the provider's own console, and it does nothing to stop someone from simply removing the lifecycle block itself (or the whole resource, or the state, or running a manual state rm then deleting by hand) before running apply. It is not a backup, and it is not a substitute for the provider's own deletion-protection features where they exist.
Analogy: A firearm's safety catch meaningfully reduces the chance of an accidental discharge during normal handling. It does not make the weapon harmless, and it is not a substitute for proper training and handling discipline - prevent_destroy is exactly this kind of layer: a real, useful tripwire against accidental destroys through the normal Terraform path, not a comprehensive protection against every possible way the underlying object could be removed.
A worked configuration
lifecycle {
create_before_destroy = true
prevent_destroy = true
}
In production, layer Terraform's prevent_destroy together with the provider's own native deletion-protection settings (many databases and storage services offer one), independent backups outside Terraform's control entirely, and least-privilege access so most people simply can't reach the destructive path in the first place. Document a clear, deliberate emergency process for the rare legitimate case where protection genuinely needs to be lifted - so that under incident pressure, someone reaches for that documented process instead of improvising risky manual state surgery.
Common mistakes in production
- Relying on
prevent_destroyalone as "the backup plan" for critical data - it only blocks the Terraform apply path, not console deletion, state manipulation, or removing the lifecycle rule itself. - Using a broad tag-based filter or blanket destroy without checking exactly which real resources it would actually match, trusting the plan summary count instead of reading the individual resource list.
Goal: Treat the plan as a change contract: understand every create, update, replacement, and destroy before approving it. Reinforce this lesson in the tf-lifecycle-safety Terraform lab. Open/labs/terraformand choose slugtf-lifecycle-safety; the lab runs real Terraform against the offline FakeCloud provider.