compliance.tf

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.

RepositoryIssueTitle
hashicorp/terraform#27360A method to override configuration and meta arguments within a module
hashicorp/terraform#24188Support for dynamic blocks and meta-arguments
hashicorp/terraform-provider-aws#19583Provider produced inconsistent final plan / an invalid new value for .tags_all

Affected resources

ResourceServiceWhy
aws_*All AWS resourcesEvery 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 an ignore_changes entry naming an attribute a resource does not have, so a module containing untaggable aws_ resources β€” aws_s3_bucket_versioning, aws_s3_bucket_policy, and most other sub-resource types β€” will fail terraform validate after this rule runs. Check the rule against a copy of your module before adopting it.
  • Terraform reports tags_all in ignore_changes as a redundant element, because the provider computes it. The warning is harmless but appears on every taggable resource the rule touches.
  • Only .tf and .tofu files 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:

FrameworkControlsRole
SOC 2CC6.1Reduces 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.

ParameterTypeDefaultDescription
ignore_changeslist(string)["tags", "tags_all"]Lifecycle attributes to ignore
resource_typeslist(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

ScenarioResult
Rule applied to a module with no aws_ resources at allNo-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 wrongFix the tag values in your Terraform config. The rule only ignores post-creation changes, not initial values.
You need Terraform to manage tags againOn 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.

On this page

Ask AI about this

Help improve this page