Ignore Tag Changes
Adds ignore_changes = [tags, tags_all] to all resources. Prevents Terraform from reverting externally managed tags.
When to use this rule
Use this when: External systems (AWS Config, Service Catalog, cost tools, or manual console edits) modify tags on your resources, causing noisy plan diffs on every terraform plan.
Do not use this when: You manage all tags exclusively through Terraform and want Terraform to enforce tag values. Or you use tags for drift detection and need Terraform to report tag changes. If only one or two known keys drift, use Ignore Selected Tag Keys, which leaves every other tag managed.
Why this rule exists
AWS tags are modified by many systems outside Terraform: AWS Config rules, Service Catalog, cost allocation tools, security scanners, and manual console edits. When Terraform detects a tag difference, it plans an update to revert the tag, even if the external change is intentional.
This creates a constant stream of noisy plan diffs. Teams using default_tags in the AWS provider are especially affected because tags_all includes the merged set of resource-level and provider-level tags.
Related Terraform and OpenTofu issues
| Repository | Issue | Title |
|---|---|---|
| hashicorp/terraform | #27360 | A method to override configuration and meta arguments within a module |
| hashicorp/terraform | #24188 | Support for dynamic blocks and meta-arguments |
| hashicorp/terraform-provider-aws | #19583 | Provider produced inconsistent final plan / an invalid new value for .tags_all |
Affected resources
| Resource | Service | Why |
|---|---|---|
aws_* | All AWS resources | Every resource type whose name starts with aws_, matched on the name alone |
What this rule does
Adds a lifecycle { ignore_changes = [...] } block to each matching resource. Terraform and OpenTofu will ignore external changes to the listed attributes on subsequent plans.
Ignored attributes: tags, tags_all
Before and after
Before (upstream module):
resource "aws_*" "this" {
# ... resource configuration ...
tags = var.tags
}After (with Ignore Tag Changes applied):
resource "aws_*" "this" {
# ... resource configuration ...
tags = var.tags
lifecycle {
ignore_changes = [tags, tags_all]
}
}The only change is the rule transformation. All existing arguments, outputs, and module behavior remain identical.
Real-world scenario
An AWS Config rule auto-tagged all resources with a ManagedBy: AWSConfig tag. Every terraform plan showed 47 tag diffs across the module. The platform team spent hours triaging 'changes' that were all false positives.
Known limits
- Does not prevent tag changes from happening. External systems can still modify tags freely.
- Does not affect the initial tag values set when a resource is first created.
- Does not cover non-AWS resources or resources from other providers.
- The
aws_*pattern matches on the resource type name, not on whether that type accepts tags. Terraform rejects anignore_changesentry naming an attribute a resource does not have, so a module containing untaggableaws_resources βaws_s3_bucket_versioning,aws_s3_bucket_policy, and most other sub-resource types β will failterraform validateafter this rule runs. Check the rule against a copy of your module before adopting it. - Terraform reports
tags_allinignore_changesas a redundant element, because the provider computes it. The warning is harmless but appears on every taggable resource the rule touches. - Only
.tfand.tofufiles are read. Modules written in JSON syntax (.tf.json,.tofu.json) are left untouched. - Only the module's own directory and its first-level
modules/*submodules are processed. Modules that this module calls by source URL are separate downloads and are handled on their own request.
Compliance framework support
This rule is not a compliance control. It supports these framework objectives as an operational safeguard:
| Framework | Controls | Role |
|---|---|---|
| SOC 2 | CC6.1 | Reduces noise in change management by preventing tag drift from triggering false-positive plan diffs |
Default configuration
These are the values the rule ships with. They are not settable from a module source URL: ?rules= carries rule names only, so a per-request enable uses exactly the defaults below. Your organization sets them per rule, in its Baseline or in a ruleset, from the Operational Rules page; see Baseline rules and rulesets.
| Parameter | Type | Default | Description |
|---|---|---|---|
ignore_changes | list(string) | ["tags", "tags_all"] | Lifecycle attributes to ignore |
resource_types | list(string) | ["aws_*"] | Target resource type patterns |
How to enable
On a framework host, per request:
Add ?rules=lifecycle_ignore_tags to your HTTPS module source. Use the full namespaced id. A bare lifecycle_ignore_tags matches no rule: it is skipped server-side and your terraform init still succeeds, without the rule.
module "example" {
source = "https://soc2.compliance.tf/terraform-aws-modules/s3-bucket/aws?version=5.0.0&rules=lifecycle_ignore_tags"
}On your organization's host:
Add the rule to the organization's Baseline, which applies to every module served from the bare organization form <alias>.compliance.tf/<namespace>/<name>/<provider>, or to a named ruleset that one module selects with ?ruleset=<name> on the HTTPS form of that address. A project environment (<alias>.compliance.tf/<project>/<env>/<module>) serves the rules frozen in its bound snapshot instead: there the rule goes into the config draft and reaches the environment by promotion. On both organization forms ?rules= is refused with a 400 β including the subtractive - prefix β rather than dropped, so you cannot mistake an ignored override for an applied one. Change the configuration, not the URL. See Baseline rules and rulesets for the configuration flow, and registry resolution for how the address forms differ.
Failure modes
| Scenario | Result |
|---|---|
Rule applied to a module with no aws_ resources at all | No-op. Module delivered unchanged. A module that does contain aws_ resources is patched on all of them, taggable or not. |
| Tags set by Terraform at creation time are wrong | Fix the tag values in your Terraform config. The rule only ignores post-creation changes, not initial values. |
| You need Terraform to manage tags again | On a framework host, add ?rules=-example/ignore_tag_changes and run terraform init -upgrade. On your organization's host that parameter is refused with a 400; take the rule out of the Baseline, or out of the ruleset the module selects, instead. |
Terraform and OpenTofu compatible
This rule works with both Terraform (1.x+) and OpenTofu (1.6+). It reads ordinary HCL, and anything it changes stays ordinary HCL, so the module behaves the same under either tool.
Help us improve this page
Operational Rules are a new feature. We'd love your feedback on this rule page β what's useful, what's missing, what's confusing. Share feedback.