Terraform derives plans from the diff between configuration (HCL), state, and the real infrastructure (remote). ignore_changes partially suppresses that diff evaluation, acting as a safety valve for accepting external changes.
Overusing it, however, leads to overlooked drift. Following the official behavior, this article succinctly summarizes when to use it, when to avoid it, comparisons with alternatives, and exam pitfalls in a practical way.
lifecycle.ignore_changes excludes diffs on the specified attributes from the plan. Even if those attributes are changed externally, Terraform will not revert them; only the state will be updated to the latest real-infrastructure values on the next refresh.
A key premise is that the behavior differs between creation and post-creation. On initial creation, the configured value (or provider default) is used, and ignore_changes only takes effect from the post-apply update phase. From then on, changes to that attribute in the config file produce no plan.
If another change requires a replacement (force new), the resource is recreated, and at that time the configured value (or default) is applied to the new resource even for attributes covered by ignore_changes.
Minimal example: suppress the diff on a specific attribute
resource "aws_instance" "web" {
ami = var.ami
instance_type = var.instance_type
tags = {
Name = "web"
Owner = var.owner
}
lifecycle {
ignore_changes = [tags] # Do not surface in plan even if external systems append tags
}
}Terraform's plan is born from the process of synchronizing 'configuration → state ←→ real infrastructure.' ignore_changes acts as a filter that 'does not put on the plan' even when diffs on target attributes are visible. Keep the following flow in mind.
1) Initial apply creates with configured values. 2) During operation, target attributes are changed externally. 3) On the next plan, refresh updates state to external values, but diffs on target attributes do not appear in the plan. 4) If a replacement is triggered by changes to another attribute, the configured values (or defaults) are used at new-resource creation.
Position of ignore_changes within diff evaluation
[Configuration]
|
v
diff calc ----> [Filter: ignore_changes on attributes]
| |
| v
[Current State] <---- refresh ---- [Remote]
|
v
[Plan] (ignored attributes: no-op)
Verify the behavior: plan is silent, state changes
# Even if tags are changed externally
terraform plan
# => No changes. (no plan because it is covered by ignore_changes)
# State is updated to external values via refresh
terraform plan -refresh-only
terraform apply -refresh-only # state only update, no resource changesUse ignore_changes only where 'coexistence' is required. Typical cases include auto-applied tags/labels, fields touched by external controllers, and values that the provider recalculates each time. On the other hand, avoid using it in areas like security or network control where consistent management by Terraform is required.
Exams test your ability to discern which attributes 'may be overwritten externally' and which 'should be fully managed.'
Example of a map you want to partially manage (tags/labels)
resource "google_compute_instance" "app" {
name = "app-1"
machine_type = "e2-medium"
# ... other required attributes ...
labels = {
owner = var.owner
role = "app"
}
lifecycle {
ignore_changes = [labels] # Do not conflict with external auto-labeling
}
}Your means depend on whether you aim to 'accept,' 'only detect,' or 'remediate' drift. Exams ask about the difference in purpose and scope. The following table organizes how to use each.
| Method | Purpose | Scope | Drift detection |
|---|---|---|---|
| lifecycle.ignore_changes | Suppress diffs (accept) | Per attribute (within a resource) | Hard to surface (does not appear in plan) |
| plan/apply -refresh-only | State sync only | Entire workspace / target resource | No (diffs are not subject to remediation) |
| terraform import | Bring existing assets under management | Per resource | Detectable from subsequent plans |
| taint / -replace | Force replacement | Per resource | — (explicit operation) |
CLI examples: state-only update / force replacement
# State-only update (real infra unchanged)
terraform plan -refresh-only
terraform apply -refresh-only
# Force replacement (comprehensive drift remediation)
terraform plan -replace=aws_instance.web
terraform apply -replace=aws_instance.web'Localization' is essential for ignore_changes. Use it only inside modules and avoid controlling it via externally exposed variables; otherwise drift becomes invisible on the caller side. Also, combine refresh-only in CI to regularly grasp how much state has changed externally.
When you inject values only at initial creation and then leave the rest to the outside, test plan stability accounting for the time gap between apply and the external overwrite.
Minimal example inside a module (AWS: tolerate attributes appended by ELB)
resource "aws_lb_target_group" "api" {
name = "api-tg"
port = 80
protocol = "HTTP"
vpc_id = var.vpc_id
# Some health-check parts may be adjusted by the ALB side
health_check {
path = "/healthz"
}
lifecycle {
ignore_changes = [health_check] # Allow external adjustments (keep to minimum)
}
}Accepting drift raises audit difficulty. Complement who/when/what changed using mechanisms other than Terraform (such as cloud audit logs). Exams often ask about the 'boundary' of which layer should perform the control.
Also, ignore_changes is not a silver bullet. Read-only attributes that the provider always recalculates internally do not appear in the plan in the first place, so no specification is needed.
Check state and the soundness of diffs
# Inspect a single state entry
terraform state show aws_instance.web
# Confirm config changes haven't rippled to other attributes
terraform plan -detailed-exitcode
# exit code 0: no changes / 2: changes present (watch for diffs other than ignore_changes)Associate / Pro
問題 1
Your organization's cloud-side policy engine automatically applies common tags to every resource. You manage some resources' tags in Terraform but want to coexist with externally added tags without removing them. Moreover, the policy is that even future changes to tag values on the Terraform configuration side should not be applied. Which response is most appropriate?
正解: A
lifecycle.ignore_changes = [tags] is ideal for requirements that exclude externally added/changed tags from the plan and do not apply future config-side changes. refresh-only only updates state and does not suppress diffs, and switching to a data source abandons resource management. taint forces replacement, which is the opposite of the requirement.
Does ignore_changes apply at initial creation?
No. On initial creation, the configured value (or the provider default) is used. ignore_changes only suppresses diffs starting from the post-apply update phase.
Can I specify ignore_changes from the module caller side?
No. lifecycle is valid only inside a resource block. To encapsulate it within a module, write lifecycle on the resources inside the module.
How do I detect drift on attributes ignored by ignore_changes?
Since it never shows up in plan, use terraform plan -refresh-only to surface state diffs, or combine with cloud audit logs and policy-engine reports. If audit requirements are strict, keep the ignored scope as small as possible.
Practice with certification-focused question sets
無料で問題を解いてみるNicheeLab Editorial Team
NicheeLab editorial team focused on data engineering and cloud certification learning. Content is structured around practical study needs and official exam domains.
HCL Syntax: Terraform's Configuration Language (2026)
HCL2 fundamentals for Terraform — blocks, attributes, expres...
Terraform Authoring & Operations Pro: Complete Guide (2026)
Tactics for the Terraform Pro exam — module authoring, works...
Terraform Providers: Plugin Management Fundamentals (2026)
Provider mechanics — required_providers, versions, mirrors, ...
Terraform Resource Blocks: Declarative Infra Units (2026)
Resource block fundamentals — addresses, references, common ...
Terraform Data Sources: Read-Only External Data (2026)
Data source basics — declaration, refresh behavior, dependen...