In Terraform, an autoscaler keeps changing an attribute that your configuration also sets, so every plan shows a diff. How does the lifecycle block's `ignore_changes` fix that, and what does it cost you?
answer
- stop fighting another controller
- suppresses the diff, not the refresh
- list attributes, or the keyword all
- create-time value still applies
- drift there is never reported again
basics
~20 signore_changes lists attributes whose real-world value Terraform should stop reacting to, so the plan no longer proposes reverting them. The cost is silence: drift on those attributes is never reported again, and the configured value is used only when the object is first created.
solid answer
~50 sYou put `ignore_changes` in the resource's `lifecycle` block with a list of attribute references — for example `ignore_changes = [desired_count]` on an `aws_ecs_service` whose scaling is owned by application autoscaling. From then on, Terraform still refreshes the object and records the real value in state, but when it builds the plan it uses the prior state value instead of your configured value for those attributes, so no diff appears and nothing gets reverted. The configured value is not useless: it is still what gets sent when the object is **created**. The cost is that you have deliberately blinded Terraform there — genuine drift on that attribute, including someone changing it by hand in the console, will never show up in a plan again. So list the specific attributes rather than reaching for `ignore_changes = all`, and treat each entry as a documented handover of ownership.
code
hcl · 10 linesresource "aws_ecs_service" "api" {
name = "api"
cluster = aws_ecs_cluster.main.id
task_definition = aws_ecs_task_definition.api.arn
desired_count = 2
lifecycle {
ignore_changes = [desired_count]
}
}go deeper
Recall that ignore_changes lists attributes Terraform should stop trying to put back, and name the typical case: something else, like an autoscaler, owns that value.
Explain the mechanics — the diff uses the prior state value, the refresh still happens, and the configured value applies only at create time — and show the named-attribute syntax including a single map key.
Demonstrate judgment about the blind spot you are creating: which attributes have a legitimate second owner, why a targeted list beats all, and how you keep an ignored attribute from hiding a real console change.
Frame it as ownership policy across the estate: agreeing which controller owns which attribute, requiring a documented reason for every ignore entry, and deciding where ignoring drift is acceptable versus where the boundary itself should be redesigned.
## The problem it solves Some attributes have two owners. You declare a desired count, a capacity, a tag set or a deployed artefact version in Terraform; then an autoscaler, a deployment pipeline, or an organisation-wide tagging policy changes it in the real world. On the next run Terraform refreshes, sees the difference between the real value and your configuration, and plans to put it back. If you apply, you fight the other system; if you do not, every plan is noisy and reviewers stop reading plans carefully — which is the more expensive failure. `ignore_changes` is the declared answer to "something else mutates this attribute". ## Syntax and semantics ```hcl lifecycle { ignore_changes = [ desired_count, tags["LastDeployed"], ] } ``` The entries are references to attributes of this resource's own configuration, written bare rather than as strings, and you can address a single map element such as `tags["LastDeployed"]` instead of the whole map. The special keyword `all` — `ignore_changes = all` — suppresses every attribute at once. Two details candidates routinely get wrong: - **It only affects updates to an existing object.** When the resource is first created, the configured value is used normally. So a service created with `desired_count = 2` and `ignore_changes = [desired_count]` starts at 2 and then never hears from Terraform about that attribute again. - **It does not stop the refresh.** Terraform still reads the object and stores the current real value in state. What it changes is the diff step: for the ignored attributes Terraform compares against the prior state value rather than your configuration, so there is nothing to propose. ## What it does to drift detection This is the sentence to say out loud: for the listed attributes, drift stops being reported at all. Not "reported and skipped" — simply absent from the plan. That is exactly what you want for an attribute another controller legitimately owns, and exactly what you do not want for an attribute a human changed at 3am in the console. Since the two look identical from inside Terraform, the size of the list is the size of your blind spot. That makes `ignore_changes = all` a heavy instrument. It is defensible for a resource Terraform should create and then never manage — an imported legacy object you are keeping in state for reference, or a resource whose whole configuration is owned elsewhere — but as a way of quietening a noisy plan it hides real regressions along with the noise. ## Common, legitimate uses - `desired_count` on `aws_ecs_service` or `desired_capacity` on `aws_autoscaling_group`, where an autoscaling policy is the real owner. - Tags injected by an external governance tool, ignored individually so the tags you do manage still get enforced. - An artefact reference — an image tag or a Lambda source hash — that a deployment pipeline updates between Terraform runs, so infrastructure changes do not roll the application back to whatever the last committed version was. - An attribute the provider itself normalises or populates in a way that produces a permanent phantom diff. ## Getting back in control Because the configured value is only applied at create time, changing your configuration for an ignored attribute has no effect. To reassert Terraform's value you remove the entry from `ignore_changes` and run a plan, which will then show the difference and let you apply it. Some teams keep the ignore permanent but move the value out of the resource and into a comment or variable documenting who the real owner is — the point is that an `ignore_changes` entry is a statement about ownership, and it belongs in review with a reason attached. ## Interaction with the rest of the lifecycle block If the attribute being ignored is one that forces replacement, ignoring it also stops the replacement being planned — that is often the actual reason for adding it, for example ignoring an `ami` change so instances are not rolled by an AMI lookup returning something new. `prevent_destroy` and `create_before_destroy` are unaffected: they act on destroys and their ordering, not on which attributes are compared.
- If ignore_changes is set on an attribute, does the value in your configuration still matter at all?Yes, at creation. `ignore_changes` only suppresses updates to an existing object, so the configured value is what the resource is created with. After that, changing it in the configuration has no effect until you remove the attribute from the ignore list.
- When is ignore_changes = all defensible rather than lazy?When Terraform's job really is create-and-forget for that object — an imported legacy resource kept in state for referencing, or a resource whose whole configuration is owned by another system. As a cure for a noisy plan it is lazy, because it hides genuine regressions along with the noise you meant to silence.
- Does ignoring an attribute also stop the refresh from reading it?No. Terraform still reads the object and records the current real value in state. The change is at the diff step: for ignored attributes it compares against the prior state value rather than your configuration, so no proposed change appears in the plan.
saying these in an interview costs you the question
- Thinks ignore_changes also applies when the resource is created
- Believes it stops Terraform refreshing that attribute
- Reaches for ignore_changes = all to quieten any noisy plan
- Assumes drift on an ignored attribute is still reported somewhere
- Expects editing the configured value to take effect while it is ignored