Enforcing AWS Tagging Standards with Terraform Tag Policies

Hardik Shah
Cloud Architect & AWS Expert

If you have ever sat through a cloud cost review where half the resources have no owner, no environment label, and zero context — you already know the pain of untagged infrastructure. AWS Tag Policies give you a way to define tagging standards at the organization level, and the Terraform AWS provider now has a built-in property that can fail your plan before a single non-compliant resource makes it to AWS.
The Problem with Voluntary Tagging
Most teams start with good intentions — a tagging guide in the internal wiki, a reminder in the PR template. But nothing technically stops someone from shipping an EC2 instance with no tags or with env=prod when your standard is Environment=production. After a few months you have a mix of inconsistent tags, unattributable costs, and compliance dashboards that are nearly impossible to trust.
What you actually need is enforcement at the code level — something that makesterraform plan refuse to proceed if the tags don't match the standard. That's exactly what tag_policy_compliance does, and it's available in the Terraform AWS provider starting from v6.22.0.
What Are AWS Tag Policies?
AWS Tag Policies live inside AWS Organizations. They let you define the rules for how resources across every account in your org should be tagged — which keys are required, what values are acceptable, and which resource types the rules apply to.
A few things worth clarifying upfront because the docs can be a bit dense on this:
- Tag Policies enforce case sensitivity. If your standard says the key is
Environment, thenenvironmentorENVIRONMENTis non-compliant. This catches the most common drift. - Policies inherit down the hierarchy. Attach a policy at the Org root and every account gets it. Attach it to an OU and only that OU's accounts are affected.
- The policy itself is just a definition. Without enforcement wired up, it reports non-compliance but doesn't block anything. The Terraform provider's
tag_policy_complianceproperty is what closes that loop.
Step 1 — Enable Tag Policies in Your Org
Before you can create any tag policies, the feature needs to be enabled in your AWS Organization. This is a one-time setup:
The feature_set = "ALL" is required. Without it, none of the policy features work. If your organization already exists, you can import it and just addTAG_POLICY to the enabled_policy_types list.
Step 2 — Define the Tag Policy
The policy content is a JSON document that uses a specific syntax with@@assign operators. The @@assign tells AWS to set a value directly (as opposed to inheriting or merging from a parent policy).
Here's an example that requires three tags — Environment, Owner, and Project — on EC2 instances, S3 buckets, and RDS databases:
What each block does:
tag_key— Sets the exact key name including casing. This is what enforcesEnvironmentvsenvironment.tag_value— Optional. When present, only the listed values are accepted. Leave this out if you want to allow any value for that key.enforced_for— Scopes the rule to specific resource types. This is important — without it, compliance is reported but the Terraform provider's shift-left check won't flag violations.
Step 3 — Attach the Policy
A tag policy only takes effect once it's attached to a target in your org hierarchy — the root, an OU, or a specific account:
Attach to a non-production OU first. The policy propagates instantly across all child accounts, so it's worth validating the impact in a sandbox before going org-wide.
Step 4 — Wire Up tag_policy_compliance in the Provider
This is the core of what makes everything click. As of Terraform AWS provider v6.22.0, there's a new tag_policy_compliance argument in the provider block. When set, Terraform evaluates your resource tags against the effective tag policy during terraform plan — before anything touches AWS.
It accepts two values:
| Value | Behavior | When to Use |
|---|---|---|
| warning | Plan succeeds but prints a diagnostic for every non-compliant resource | Roll-out phase — surface violations without breaking existing pipelines |
| error | Plan fails immediately if any resource violates the tag policy | Enforcement phase — hard block for all IaC workflows |
When tag_policy_compliance = "error" is set and a resource is missing a required tag — say an EC2 instance without an Owner tag — the plan output will look something like this:
No AWS API call was made. The resource never existed. The developer sees the exact tag that's missing and fixes it in the PR before it ever gets reviewed or merged.
How the IAM Permission Works
For the provider to check tag compliance during plan, the IAM principal running Terraform needs the organizations:ListRequiredTags permission. This is how the provider fetches the effective tag policy for the account. Make sure your pipeline role or your local AWS profile includes this:
Recommended Rollout Strategy
Jumping straight to error mode on an existing codebase is going to break a lot of pipelines at once. This is the sequence that works well:
- Create the tag policy and attach it to a sandbox OU. Let the compliance report run for a few days in the AWS console. Understand what's actually non-compliant before you plug in any enforcement.
- Set
tag_policy_compliance = "warning"across all repos. This surfaces violations in CI pipeline logs without failing any runs. Share the report with your teams and give them a sprint to fix their missing tags. - Flip to
erroronce the backlog is clean. At this point, every new resource that goes through Terraform will be validated against the policy. Tag drift is blocked at the source. - Attach the policy to the org root. Once you've validated behavior at the OU level, expand the coverage to all accounts.
Things to Watch Out For
- Provider version matters. The
tag_policy_complianceargument doesn't exist below v6.22.0. If you don't pin your provider version, you might not get this behavior in all environments. Always useversion = ">= 6.22.0". - The
enforced_forfield is required for the shift-left check. If your tag policy doesn't have anenforced_forblock for a resource type, the Terraform provider won't flag that resource type during plan, even in error mode. - Case sensitivity is the most common gotcha. If the policy says
Environmentand your Terraform hasenvironment, it will fail. Document the exact casing in your team's standards and be consistent. - Default tags in the provider don't automatically satisfy policy. If you use
default_tagsin the provider block, those tags are evaluated too — but make sure the values match the allowed list in the policy.
Wrapping Up
The combination of AWS Tag Policies and the tag_policy_compliance provider property is a genuinely clean solution to a problem that most cloud teams deal with for years before getting serious about fixing. You define the standard once in your org, you wire up one property in your provider block, and from that point forward Terraform is your first line of enforcement — not an audit report two weeks after the fact.
Start in warning mode, clean up the existing violations, then flip toerror. The migration is low risk and the payoff — clean cost attribution, trustworthy compliance reports, and consistent resource metadata across every account — is well worth the effort.

About Hardik Shah
Hardik is a dedicated Cloud Architect specializing in AWS solutions and DevOps automation. With years of industry experience, he focuses on building scalable, resilient architectures and sharing technical insights to help teams optimize their cloud-native journeys.