ignore_changes and Shared Ownership
Scenario: An autoscaler legitimately adjusts a server fleet's capacity every few minutes in response to real load, and every single Terraform plan afterward proposes shrinking it right back down to whatever fixed number is written in the configuration.
ignore_changes, set inside a lifecycle block, tells Terraform: consider this specific argument's value when the resource is first created, but afterward, stop treating any live difference on that one field as something to reconcile. This is the correct, deliberate tool for exactly the scenario above - a case of genuinely shared ownership, where some other system (an autoscaler, a manual runbook that legitimately tunes one setting) is meant to actively control a specific field going forward, and Terraform should stop fighting it over that one attribute.
The honest trade-off: every field you add to ignore_changes becomes permanently invisible to Terraform's normal drift detection (Module 5) - which is exactly the point when the ownership genuinely is shared, but also exactly the risk if you reach for it too broadly. Scope it to the single narrowest attribute that actually needs to be shared (desired_capacity, not the whole resource), write a comment naming which external system owns that field and why, and treat ignore_changes = [all] - ignoring literally everything about a resource - as a red flag reserved for carefully bounded, temporary transitions, never a permanent convenience.
Analogy: Two pilots cannot both hold the same physical control at the same time without fighting each other for it. The sane arrangement is to explicitly assign the autoscaler ownership of the throttle for that one specific control, while Terraform continues to own the aircraft's overall configuration - each system has a clearly scoped, non-overlapping area of authority.
A worked configuration
lifecycle {
ignore_changes = [desired_capacity]
}
Because every ignored field silently drops out of your normal plan-time visibility, it deserves independent monitoring outside Terraform - a field that's meant to be autoscaler-owned capacity could just as easily start drifting because of a security misconfiguration or unexpected cost change, and Terraform will now never mention it. Periodically revisit every ignore_changes entry in a codebase and confirm the shared-ownership arrangement it represents is still actually true and still actually needed - these tend to accumulate and outlive the reason they were originally added.
Common mistakes in production
- Reaching for
ignore_changes = [all]as a quick fix for one noisy field, instead of scoping it to just that specific attribute - this silences drift detection for the entire resource, not just the intended field. - Adding
ignore_changeswithout a comment explaining which external system now owns that field - six months later nobody remembers why, and nobody notices if that external system stops existing.
Goal: Treat the plan as a change contract: understand every create, update, replacement, and destroy before approving it. Reinforce this lesson in the tf-lifecycle-safety Terraform lab. Open/labs/terraformand choose slugtf-lifecycle-safety; the lab runs real Terraform against the offline FakeCloud provider.