Ignore selected tag keys
For platform engineers deciding whether to apply lifecycle_ignore_tag_keys: what it changes, how to enable it, and how to check it worked.
Adds ignore_changes = [tags["<key>"]] for each tag key your organization configures. Drift on those keys is ignored; every other tag stays managed by Terraform.
Use this when: One or two external systems, such as a security scanner or an inventory agent, write their own tag keys onto resources, and you want Terraform to keep managing every other tag.
Do not use this when: Tags drift on many keys from many sources; Ignore tag changes ignores tags and tags_all wholesale. Or you rely on tag drift showing up in terraform plan as a detection signal.
What this rule does
Adds a lifecycle { ignore_changes = [tags["<key>"]] } entry to each matching resource, one entry per key in tag_keys. Terraform and OpenTofu then ignore external changes to those keys and keep managing every other tag. A resource that already carries an ignore_changes list keeps it; the keys are merged in.
Ignored keys: the tag_keys your organization configures. The shipped default is empty, so the rule does nothing until it is configured.
Affected resources
| Resource | Service | Why |
|---|---|---|
aws_s3_bucket | Amazon S3 | The default target. Add other taggable types through resource_types. |
Before and after
Before (a module without the change):
resource "aws_s3_bucket" "this" {
# ... resource configuration ...
tags = var.tags
}After (with Ignore selected tag keys applied, tag_keys = ["scanner-last-seen"]):
resource "aws_s3_bucket" "this" {
# ... resource configuration ...
tags = var.tags
lifecycle {
ignore_changes = [tags["scanner-last-seen"]]
}
}The key list is whatever your organization configured; the one above is an example.
The only change is the rule transformation. All existing arguments, outputs, and module behavior remain identical.
Why this rule exists
Some tags are owned by a system other than Terraform: a vulnerability scanner records when it last inspected a bucket, an inventory agent stamps an asset id, a cost tool writes an allocation code. Terraform sees each of those as drift and plans to remove the tag on the next run.
Ignore tag changes answers that by ignoring tags and tags_all wholesale, which also silences every tag Terraform is supposed to manage. This rule ignores only the keys you name. The keys travel as a parameter of the organization's configuration, never in a module source URL, and the registry writes them into HCL as literal map keys, so a key cannot carry anything but a tag key.
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 |
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.
| Parameter | Type | Default | Description |
|---|---|---|---|
tag_keys | list(string) | [] | AWS tag keys to ignore drift on. Each is emitted as ignore_changes = [tags["<key>"]] with safe literal encoding; empty = unconfigured no-op |
resource_types | list(string) | ["aws_s3_bucket"] | Target resource type patterns. A module with none of these is not applicable |
The HCL this rule is written in, with its parameters and the transformers it runs, is published at lifecycle_ignore_tag_keys.
How to enable
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. The rule's entry carries an outcome: enforced means an ignore_changes entry was written; not_applicable means nothing was written, either because no resource of a targeted type was present or because tag_keys was empty. When the rule reached the module through a ruleset, the entry also carries ruleset.name and ruleset.content_hash. Do not infer enforcement from a successful terraform init; a rule that enforces nothing also inits cleanly.
Known limits
- Only the keys listed in
tag_keysare ignored. Every other key stays managed, and the value Terraform sets at creation time is never changed. - Applies to the resource types in
resource_types(defaultaws_s3_bucket), matched on the type name. Other taggable types must be added explicitly. - Cannot be selected from a module source URL.
?rules=accepts rule names without a dot, and the rule's defaulttag_keysis empty, so it only does something through your organization's rule configuration. - A key outside the AWS tag-key character set is rejected when the rule is saved. A value the registry cannot encode is dropped, and the rule falls back to its empty default, rather than written into HCL.
- 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. - 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
| Scenario | Result |
|---|---|
Rule enabled with an empty tag_keys | No-op. Nothing is added, and the manifest records the rule as not_applicable, the same outcome as a module with no matching resource. Tell the two apart from the configuration, not the manifest. |
| Module has no resource of a configured type | No-op. Module delivered unchanged; the manifest marks the rule not_applicable. |
The resource already has a lifecycle { ignore_changes = [...] } list | The key is merged into the existing list. Entries already there are kept. |
| You need Terraform to manage that key again | Remove the key from tag_keys, or move the rule from the Baseline into a ruleset that only the affected modules select. On your organization's host a URL parameter cannot remove it: ?rules= is refused with a 400. |
Related
- Ignore tag changes
- Rulesets in the console - where an org admin adds this rule to the Baseline or a ruleset
- Rule catalog - every rule, with what each one changes