Sign in
Menu
Sign up
Docs
Operational rulesRule catalog

Ignore tag changes

For platform engineers deciding whether to apply lifecycle_ignore_tags: what it changes, how to enable it, and how to check it worked.

Adds ignore_changes = [tags, tags_all] to all resources. Prevents Terraform from reverting externally managed tags.

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

Affected resources

ResourceServiceWhy
aws_*All AWS resourcesEvery resource type whose name starts with aws_, matched on the name alone

Before and after

Before (a module without the change):

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.

The example above is illustrative. To see this rule run for real, open it in the Rules Playground: it applies lifecycle_ignore_tags to a reviewed upstream module that declares the resources this rule targets, and shows the diff the registry would serve. No account needed.

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

Limitations this rule answers

Per-module pages under Workarounds where this rule is the answer, each with the native workaround first and the diff the rule makes:

Default configuration

These are the values the rule ships with. Parameters never travel in a module source URL, so a rule named there runs with exactly these defaults. Your organization sets them per rule, in its Baseline or in a ruleset.

ParameterTypeDefaultDescription
ignore_changeslist(string)["tags", "tags_all"]Lifecycle attributes to ignore
resource_typeslist(string)["aws_*"]Target resource type patterns

The HCL this rule is written in, with its parameters and the transformers it runs, is published at lifecycle_ignore_tags.

How to enable

On a framework host, add the rule on top of your organization's Baseline with ?add_rules= in the HTTPS module source:

module "s3_bucket" {
  source = "https://soc2.compliance.tf/terraform-aws-modules/s3-bucket/aws?version=x.y.z&add_rules=lifecycle_ignore_tags"
}

On your organization's host, an org admin adds the rule to the Baseline, which reaches every module served from that host, or to a named ruleset that a module selects with ?ruleset=:

module "s3_bucket" {
  source = "https://bobthecorp.compliance.tf/terraform-aws-modules/s3-bucket/aws?version=x.y.z&ruleset=<ruleset-name>"
}

Replace x.y.z with the release you use and bobthecorp with your organization's alias. Which parameters each host accepts, and which it refuses with a 400, is on Rulesets in the console.

How to check it worked

Read compliancetf-manifest.json in the module you received. Its entry for this rule carries an outcome: enforced means the change was made, not_applicable means nothing matched - no resource of a targeted type, or the module already carried the identical change. Do not infer enforcement from a successful terraform init; a rule that changes nothing also inits cleanly.

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. The rule writes tags and tags_all only on types whose schema has them, so untaggable sub-resources such as aws_s3_bucket_versioning and aws_s3_bucket_policy are skipped. A resource type newer than the rule engine's schema index keeps both entries, and Terraform rejects an ignore_changes entry naming an attribute the resource does not have, so check the rule against a copy of a module that uses a newly released resource type.
  • 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.
  • Works with Terraform 1.x and OpenTofu 1.6 and later. The rule reads ordinary HCL, and anything it changes stays ordinary HCL.

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=-lifecycle_ignore_tags and run terraform init. 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.

On this page

Ask AI about this

Help improve this page