What does the Terraform lifecycle argument `replace_triggered_by` do, and why is it often paired with a `terraform_data` resource?
answer
- a dependency for freshness, not for data
- references to resources only
- no variables or locals allowed
- wrap the value in terraform_data
- the modern null_resource triggers trick
basics
~20 sreplace_triggered_by lists other resources or their attributes, and replaces this resource whenever one of them changes. It accepts only resource references, so a plain value such as a variable has to be wrapped in a terraform_data resource first.
solid answer
~50 s`replace_triggered_by`, added in Terraform 1.2, takes a list of references to managed resources, resource instances or their attributes, and forces Terraform to replace *this* resource whenever any of them changes or is itself replaced. It is the declarative version of "roll the instances whenever the launch configuration changes" — a dependency that exists for redeployment rather than for reading a value. The restriction that trips people up is that it only accepts references to managed resources: you cannot point it at a variable, a local or a data source. That is where `terraform_data` comes in — a built-in resource, available since Terraform 1.4, whose `input` argument holds whatever value you want to key on. You feed the variable into `terraform_data`, then reference that resource from `replace_triggered_by`, and a change to the value now propagates as a replacement. Before 1.4 people used `null_resource` with `triggers` for the same trick.
code
hcl · 12 linesresource "terraform_data" "app_revision" {
input = var.app_revision
}
resource "aws_instance" "app" {
ami = var.ami_id
instance_type = "t3.small"
lifecycle {
replace_triggered_by = [terraform_data.app_revision]
}
}go deeper
Recall that it forces this resource to be recreated when another resource it names changes, and that the trigger must be a resource reference, not a variable.
Explain the reference restriction and the terraform_data bridge — input stores a value in state so a variable change becomes a detectable resource change — and name the version each feature needs.
Weigh it as an operational lever: it destroys and recreates real infrastructure on a value change, so key it as narrowly as possible and prefer a platform-native rollout where one exists.
Decide where Terraform-driven replacement is the right redeployment mechanism at all, versus letting a deployment system own application rollout while Terraform owns only the substrate.
## The problem Terraform's dependency graph is built from references: if resource B reads an attribute of resource A, B is created after A, and B is updated when the value it read changes. That covers the case where the dependency is about *data*. It does not cover the case where the dependency is about *freshness* — "whenever the configuration this thing was built from changes, rebuild this thing", even though the change does not appear in any attribute the resource actually reads. The usual workarounds were ugly: a `null_resource` with `triggers` calling a provisioner, or a fake attribute change smuggled into a tag. `replace_triggered_by` makes it a first-class lifecycle setting. ## How it works ```hcl lifecycle { replace_triggered_by = [ terraform_data.app_revision, ] } ``` The list accepts references to managed resources, to a specific instance of one, or to an attribute of one. When the referenced item changes — the attribute takes a new value, or the referenced resource is itself replaced — Terraform plans a replacement of the resource carrying the lifecycle block. In the plan output it shows up with the reason that it is being replaced because of a change in the referenced item, which is much easier to review than a mysterious tag diff. What it does **not** accept is any other kind of expression. Variables, locals, data sources and arbitrary function calls are all rejected. The restriction is deliberate: the reference has to be to something Terraform tracks in state, so it can tell that it changed between runs. ## Why terraform_data `terraform_data` is a built-in resource type — no provider required — introduced in Terraform 1.4 as the modern replacement for the old `null_resource` from the null provider. Its `input` argument stores an arbitrary value in state and exposes it as `output`. That gives you a stateful, referenceable handle for a value that would otherwise be just a variable: ```hcl resource "terraform_data" "app_revision" { input = var.app_revision } ``` Now `terraform_data.app_revision` is a managed resource whose recorded value changes when the variable changes, so it is a legal target for `replace_triggered_by`. Changing `var.app_revision` from `v7` to `v8` updates the `terraform_data` object and, through the trigger, replaces the instance. On Terraform versions before 1.4 the same shape was written with `null_resource` and its `triggers` map. ## When to reach for it, and when not to Good fits are genuinely coupled lifecycles: an instance that must be rebuilt when a bootstrap artefact version changes; a resource that has to follow the replacement of something it is paired with but does not reference; an appliance whose configuration is delivered out of band and only takes effect on recreation. Be careful, though. Every entry is a lever that destroys and recreates real infrastructure on a value change, and it is easy to attach it to something that changes more often than you expect — then an unrelated variable edit rolls the fleet. Two habits help: key on the narrowest attribute rather than a whole resource, and, where the platform can roll the change itself, prefer that to Terraform-driven replacement. ## Version notes `replace_triggered_by` requires Terraform 1.2 or later; `terraform_data` requires 1.4 or later. On older versions the equivalent is `null_resource` with `triggers`. It is worth stating the version assumption in an interview, because this is one of the parts of the language that moved recently.
- Why can replace_triggered_by not just reference a variable directly?Because it needs something Terraform tracks between runs. Managed resources have prior state, so Terraform can tell the referenced value changed; a variable has no recorded previous value to compare against. Wrapping it in a `terraform_data` resource gives the value a state entry and therefore a detectable change.
- How does replace_triggered_by differ from an ordinary implicit dependency?An implicit dependency comes from reading an attribute, and normally results in an in-place update when that value changes. `replace_triggered_by` creates an ordering relationship whose consequence is a full replacement, and it works even when this resource never reads anything from the referenced one.
saying these in an interview costs you the question
- Thinks it accepts any expression, including variables and locals
- Confuses it with a forced replacement of the referenced resource
- Believes it causes an in-place update rather than a replacement
- Keys it on a whole resource that changes far more often than expected
- Assumes terraform_data needs the null provider installed