#4569·opentofu

prevent_destroy from a variable makes destroy unplannable when a count/for_each module instance is removed

Author: norm-dole-dbtlabsCreated Sep 9, 2026Updated Sep 17, 2026

Community note

Filing this as a distinct case from #3504, which was fixed by #3507 and covered tofu destroy with the configuration intact. That path works correctly today. The case below is not covered by that fix.

OpenTofu Version

bash
OpenTofu v1.12.3
on darwin_arm64

# also reproduced on:
OpenTofu v1.13.0-beta1
on darwin_arm64

No providers are required — the repro uses the built-in terraform_data.

OpenTofu Configuration Files

mod/main.tf:

hcl
variable "name" { type = string }

variable "prevent_destroy" {
  type    = bool
  default = true
}

resource "terraform_data" "z" {
  input = var.name
  lifecycle {
    prevent_destroy = var.prevent_destroy
  }
}

main.tf:

hcl
variable "enabled" {
  type    = bool
  default = true
}

module "single" {
  count = var.enabled ? 1 : 0

  source          = "./mod"
  name            = "x"
  prevent_destroy = false # literal, supplied by the caller
}

Debug Output

Error: Invalid value for prevent_destroy

  on mod/main.tf line 11, in resource "terraform_data" "z":
  11:     prevent_destroy = var.prevent_destroy
    ├────────────────
    │ var.prevent_destroy is a bool, known only after apply

Resource instance module.single[0].terraform_data.z has a prevent_destroy
argument but its value will not be known until the apply step, so OpenTofu
can't predict whether destroying this is acceptable.

To proceed, exclude instances of this resource from this round using:
    -exclude="module.single[0].terraform_data.z"

Expected Behavior

The plan should succeed and show the resource being destroyed.

The documentation for prevent_destroy states:

It does not prevent destruction if the resource block is removed from the configuration.

Dropping a count to 0 (or removing a for_each key) removes that instance's configuration, so this should fall under the documented "removed from the configuration" case. It also already behaves that way when the entire module block is deleted — see the asymmetry below.

Note also that the caller supplies a literal false. Even if the value were evaluated, it is statically known and would permit the destroy.

Actual Behavior

The plan fails with Invalid value for prevent_destroy. Because this is a plan-time error, the resource cannot be destroyed through a configuration change at all — the whole plan is rejected, not just that resource.

Steps to Reproduce

  1. tofu init
  2. tofu apply -auto-approve (creates module.single[0].terraform_data.z)
  3. tofu plan -var 'enabled=false'
  4. Observe the error above.

The same happens with for_each when a key is removed from the map.

Additional Context

1. Asymmetry with deleting the module block. With byte-identical state, the outcome depends on whether the module block still exists:

Configuration change Result
Module block kept, count 10 Invalid value for prevent_destroy
Module block kept, for_each key removed Invalid value for prevent_destroy
Module block deleted entirely ✅ plans clean, 1 to destroy
tofu plan -destroy, config intact, variable true ✅ correct Resource instance cannot be destroyed error
tofu plan -destroy, config intact, variable false ✅ plans clean, 1 to destroy

Deleting the whole block takes a path that does not attempt evaluation, so the destroy proceeds. Keeping the block but removing the instance attempts to evaluate prevent_destroy in a module-instance scope that no longer exists, and the absent value surfaces as unknown.

2. A caller-supplied literal does not help. In the repro above the caller passes prevent_destroy = false as a constant. It makes no difference, because the argument belongs to a module instance that no longer exists in configuration.

3. The suggested -exclude address does not work. Passing exactly the address the error prints reproduces the same error and the same suggestion:

tofu plan -var 'enabled=false' -exclude='module.single[0].terraform_data.z'
# -> same "Invalid value for prevent_destroy" error

Excluding at the module address does work:

tofu plan -var 'enabled=false' -exclude='module.single'
# -> plans successfully

This may warrant a separate issue; flagging it here since the error message actively directs users to a form that fails.

4. Practical impact. Any module invoked with count or for_each that sets lifecycle.prevent_destroy from a variable cannot be torn down through an ordinary configuration change. The failure only appears at teardown time, long after the pattern is adopted, and the message ("known only after apply") points toward a timing problem rather than the actual cause.

References

  • #1329 — parent feature request for variables in lifecycle attributes (still open, pending-decision)
  • #3504 — closely related, fixed by #3507; covers tofu destroy with configuration intact, not this case
  • #3507 — tofu: GraphNodeDestroyer can now have references
  • Docs: https://opentofu.org/docs/language/resources/behavior — "It does not prevent destruction if the resource block is removed from the configuration."