Live
Team plans can now self‑start GitHub Advanced Security trialsTerraform 1.16 brings destroy actions and module-level imports into the resource lifecycleBuilding an AI‑Enabled Media Ecosystem: What Engineers Need to KnowEdge developers can now use post‑quantum ML‑KEM and ML‑DSA primitives in Cloudflare WorkersX25519 TLS support removed from GitHub Enterprise Cloud – what engineers need to knowDurable Objects Remain Active During Unattached I/O TasksCloudflare WAF blocks new GitLab path traversal and tightens request‑smuggling rulesAI‑Assisted DevOps Awards Expand: Practical Implications for Engineers and ArchitectsTeam plans can now self‑start GitHub Advanced Security trialsTerraform 1.16 brings destroy actions and module-level imports into the resource lifecycleBuilding an AI‑Enabled Media Ecosystem: What Engineers Need to KnowEdge developers can now use post‑quantum ML‑KEM and ML‑DSA primitives in Cloudflare WorkersX25519 TLS support removed from GitHub Enterprise Cloud – what engineers need to knowDurable Objects Remain Active During Unattached I/O TasksCloudflare WAF blocks new GitLab path traversal and tightens request‑smuggling rulesAI‑Assisted DevOps Awards Expand: Practical Implications for Engineers and Architects
HashiCorp

Terraform 1.16 brings destroy actions and module-level imports into the resource lifecycle

AI SummaryPowered by AI

Terraform 1.16 adds beforedestroy and afterdestroy events to the actiontrigger lifecycle block and allows import blocks to be defined inside child modules. These changes let operators embed teardown automation and adoption logic directly where the resources live, reducing external scripts and simplifying module reuse.

Terraform 1.16 introduces two practical extensions that affect how you manage resource lifecycles and module adoption: the ability to run provider‑defined actions before and after a resource is destroyed, and the permission to place import blocks inside child modules. Both changes reduce the need for external scripts or root‑module workarounds, letting the configuration that owns a resource also own its teardown and adoption steps.

What Changed in Terraform 1.16

Prior to this release, the action_trigger block could only react to create and update events. The new before_destroy and after_destroy events let a provider expose an imperative operation that Terraform will schedule alongside the resource’s deletion. A typical pattern looks like this:

action "example_cleanup" "archive" {
  config { resource_id = caller.id }
}

resource "example_service" "payments" {
  name = "payments"
  lifecycle {
    action_trigger {
      events  = [before_destroy]
      actions = [action.example_cleanup.archive]
    }
  }
}

When the example_service.payments resource is slated for removal, Terraform will invoke the example_cleanup.archive action before the actual destroy operation. The after_destroy counterpart works similarly for post‑deletion cleanup.

In parallel, import blocks are no longer forced into the root module. A child module can now declare an import that targets a resource defined within the same module, for example:

variable "existing_bucket_name" { type = string }
resource "aws_s3_bucket" "this" { bucket = var.existing_bucket_name }
import { to = aws_s3_bucket.this id = var.existing_bucket_name }

The calling configuration supplies the bucket name, and Terraform evaluates the import for each module instance, including those created with count or for_each. The import appears in the plan, giving operators a chance to review the state transition before applying.

Why It Matters for Engineers

AI and data‑pipeline teams often need to snapshot databases or deregister models before tearing down compute resources. The new destroy actions let those steps be expressed as provider‑native operations, avoiding ad‑hoc scripts that sit outside Terraform’s audit trail.

Cloud and platform engineers gain tighter encapsulation of lifecycle logic. A module that provisions a managed service can now declare its own backup or inventory cleanup, making the module reusable across environments without extra root‑module glue.

DevOps and SRE practitioners benefit from a single source of truth for both creation and destruction. The planning engine now knows that a destroy action must be fully defined at plan time, which enforces deterministic behavior and reduces surprise failures during automated pipelines.

Security engineers see the same benefit: teardown steps such as revoking certificates or removing secrets can be codified and reviewed in the same plan that removes the resource, ensuring that no orphaned artifacts remain.

Operational and Security Implications

  • Planning constraints: The configuration for a destroy action must be known during the plan phase. Ephemeral values cannot be used, and the action_trigger block must stay in the configuration until the destroy is applied. A common migration pattern is to set count = 0 on the resource while keeping the trigger, then delete the block in a later change.
  • Failure handling: Terraform 1.16 supports three modes—halt (default), continue, and taint. halt stops dependent processing on error; continue logs a warning and proceeds; taint marks a newly created resource for replacement but does not make a failed destroy retryable. Choose the mode that aligns with your risk tolerance.
  • Module reuse: By moving import blocks into child modules, teams can ship a module that both creates and adopts existing resources. Consumers only need to provide the external identifier, reducing duplication of internal resource addresses.
  • State visibility: Imports remain visible in the plan, preserving the review step that Terraform users rely on. However, the provider must still support the import operation; Terraform does not add new import capabilities beyond what the provider offers.

Related CloudNinjas coverage: hands-on guides.

What This Means For Practitioners

Update your provider versions to ensure they expose the new actions you need, and add before_destroy or after_destroy blocks where teardown logic belongs. When refactoring modules, move any import declarations into the module that defines the resource, and pass the required identifiers as variables. Review your failure‑mode settings to match operational expectations, and test destroy plans in a sandbox to confirm that required values are static. By embedding both cleanup and adoption steps directly in Terraform, you tighten auditability, reduce external orchestration, and make modules truly portable across teams and environments.

Originally published atHashiCorp Blog