Skip to content

Build1 publisherNot yet confirmed elsewhere3 min readPublished

Terraform 1.16 moves pre-destroy cleanup scripts into the plan with destroy-time Actions

Terraform 1.16 lets Actions fire on before_destroy and after_destroy events, putting backups and deregistration beside the resource and in the plan. Pipelines pay for that with two-step deletions and a failure mode someone has to choose on purpose.

The Engineer · Build desk

How we use AISend a correction

Illustration accompanying Terraform 1.16 moves pre-destroy cleanup scripts into the plan with destroy-time Actions
Generated illustration

What happened

  • Terraform 1.16 also lets child modules carry their own import blocks, so the root module no longer has to own every import.
  • Each module-owned import is evaluated per module instance, including nested modules and module calls that use count or for_each.
  • A destroy Action's configuration and its trigger condition must be fully known at plan time.
  • Action failures are handled in one of three modes, halt, continue or taint, and halt is the default.
  • The release also adds JSON output for state show and workspace list, Mermaid output for terraform graph, and Linux s390x binaries.

Compiled by The EngineerSomething wrong?How this is made

Why it matters

  • constraint Cleanup that depends on values resolved during apply cannot move into a destroy Action until the configuration is restructured to know those inputs at plan time.
  • cost Every deletion of a hooked resource now costs two reviewed changes plus a note telling reviewers why the first one exists.
  • decision Each destroy Action forces a choice between a teardown that stops on a failed backup and one that proceeds without it.
  • capability Authors of shared module catalogues can build adoption of existing infrastructure into the module once, instead of each consumer writing root-level imports.

An Action is its own block. It attaches to a resource through action_trigger inside that resource's lifecycle block, with before_destroy or after_destroy listed in events [4][1]. HashiCorp's release post leads with a final backup, deregistering an asset, and cleaning up an external system before the infrastructure goes away, according to a dev.to write-up of the post [3]. The write-up's author used to run those steps outside Terraform, in a script called pre-destroy.sh that took a backup, poked an external inventory system and only then let the pipeline run terraform destroy [19]. "Everyone on the team was a little scared of it, and nobody ever fully trusted it," the author wrote [18].

The trigger hangs off the resource block, and that placement sets the constraint teams will hit first. Delete the block and its action_trigger goes with it, which leaves Terraform no hook to call when it plans the destroy [10]. HashiCorp recommends two steps: first put the resource and its trigger configuration at count = 0, and only remove the block in a follow-up change [11]. The destroy and the hook run in the first change. The second deletes a block that no longer manages anything [10][11]. A reviewer scanning the diff for deleted resource blocks will find none in the change that does the deleting [11].

Failure handling needs the same care. Under continue, Action errors become warnings, so a backup that fails during teardown does not stop the destroy [12][14]. The taint mode offers no rescue. According to the write-up, taint flags a freshly created resource to be replaced, and it does nothing to let a failed destroy Action be retried [13]. For a backup Action I'd keep halt, which stops dependent processing after an error, and accept a stuck pipeline over a teardown with no backup [12].

Terraform has long had destroy-time provisioners, set with when = destroy, which run shell commands as a side effect of the destroy [15]. "They work, but a provisioner is an escape hatch and reviewers tend to treat it as one," the author wrote [21]. CloudFormation covers backups with DeletionPolicy: Snapshot on supported resources and uses custom resources for other cleanup on delete [16]. Kubernetes finalizers block deletion of an object until a controller has done its cleanup [22]. In Terraform's version, the cleanup step sits next to its resource and shows up at plan time, per the write-up [5].

"The second change is quieter, and I think more people will feel it day to day," the author wrote of the import change [23]. Previously, an import block was allowed to point at a resource in a child module, yet the block had to be declared in the root module [6]. HashiCorp's post calls that "an awkward exception" for modules that are supposed to hide their implementation details [7]. In the write-up's example, a log-bucket module declares its own import keyed on a bucket-name variable, and the caller passes only the name, with no resource addresses [20].

I'd adopt module-owned imports in a shared catalogue at the next version bump. Destroy Actions fit where cleanup inputs are known at plan time and the team will accept two-step deletions [9][11].

What to watch

  • Whether a later Terraform release lets a trigger fire from a deleted resource block, which would remove the count = 0 step.
  • Which providers ship real backup and deregistration Action types; the release example uses placeholder example_cleanup and example_service names.
  • Whether HashiCorp adds a retriable failure mode for destroy Actions, since taint does not cover a failed teardown.

Clarity's read

What the record supports and how the coverage leans. The claims behind it follow.

Reality

Evidence55
Adoption
Insufficient
Hype gap0
Incentives
Insufficient
Confidence50
Why these scores

Claim ledger

Ranked by verification strength, evidence, and original report placement.

  1. [1]

    Terraform 1.16 lets Actions fire on before_destroy and after_destroy events.

    ReportedSupportedSource: dev.to write-up summarising HashiCorp's release postView cited source
  2. [2]

    In Terraform 1.16, import blocks can live inside child modules, so the root module no longer has to own every import.

    ReportedSupportedSource: dev.to write-up summarising HashiCorp's release postView cited source
  3. [3]

    HashiCorp's release post opens with use cases of taking a final backup, deregistering an asset, or cleaning up an external system before the infrastructure goes away.

    ReportedSupportedSource: dev.to write-up citing HashiCorp's release postView cited source

Sources

1 independent publisher whose own reporting we read for this story.

  1. dev.to

    1 article · October 6, 2026

    Terraform 1.16 adds destroy-time Actions and lets child modules own their imports

Share your take

Let Clarity write the post for you.

Signed-in readers get a short post drafted on this story in the register they choose — narrative, analytical, or a direct position — editable to the last word before it goes anywhere. The share buttons at the top of this story work without an account.

Topics and entities

Follow any of these and your For You feed starts watching them — no settings page required.

Topics

Loading related stories