terraform_data and Triggers

Scenario: A deployment needs a small integration step - say, notifying a downstream system - to rerun specifically when a release's checksum changes, but there's no actual provider-managed resource that naturally represents "a release happened."

The built-in terraform_data resource (which ships with Terraform itself, needing no external provider at all) exists to solve exactly this shape of problem: it holds arbitrary values and lifecycle triggers without corresponding to any real infrastructure object. Its most useful argument, triggers_replace, lets you declare "replace this resource - and, if absolutely necessary, re-run any provisioner attached to it - whenever this computed value changes," which is precisely how you'd model "rerun this step when the release checksum changes" without inventing a fake resource just to hang that logic on.

terraform_data is a genuine improvement over the older, now-legacy null_resource pattern specifically in how it participates in the dependency graph - it behaves more predictably as a real graph node other resources can depend on. But it's worth being honest about its limits: it does not turn an attached provisioner's arbitrary script into something declarative or independently observable - all the previous section's cautions about opaque scripts still apply in full to whatever provisioner you attach here. terraform_data solves "when should this trigger," not "what does the triggered script actually do, and can Terraform verify it worked."

Analogy: A terraform_data trigger is a dated work order pinned to a contractor's schedule - it precisely records when the contractor should be called out. It says nothing at all about what actually happened once the contractor's van pulled away and the work began; that part remains just as opaque as any other provisioner.

A worked configuration

resource "terraform_data" "release_hook" {
  triggers_replace = [filesha256("release.json")]
  provisioner "local-exec" { command = "./publish-release.sh" }
}

Whenever the operation you're modeling has a genuine, durable lifecycle of its own - something that should be created once, tracked, and potentially rolled back - prefer a real provider resource or a dedicated CI pipeline step over bolting it onto a terraform_data trigger. A strong warning sign that an integration has outgrown this pattern: if recovering from a failure ever requires manually editing Terraform state to get back to a working plan, that operation almost certainly belongs somewhere other than inside Terraform.

Common mistakes in production

Goal: Treat the plan as a change contract: understand every create, update, replacement, and destroy before approving it. Reinforce this lesson in the tf-terraform-data-triggers Terraform lab. Open /labs/terraform and choose slug tf-terraform-data-triggers; the lab runs real Terraform against the offline FakeCloud provider.