I spent a frustrated afternoon waiting for our tag policies to prevent engineers from creating EC2 instances without an Environment tag. I’d attached the policy, but instances kept launching without tags. My mistake: I thought tag policies prevent resource creation like SCPs do. They don’t. Tag policies define tagging standards for compliance reporting, not preventive controls. In this post, I’ll walk through exactly what tag policies do (and don’t) do, and how to enforce tagging correctly.
The Problem
Your AWS Organizations has a tag policy attached to an OU, but:
- Resources are still being created without required tags
- The policy doesn’t block non-compliant tagging
- Engineers can create EC2 instances, S3 buckets, etc. without any tags
- Tag policies seem to have no effect on resource creation
Why Does This Happen?
Tag policies are not preventive controls. They’re compliance reporting tools. Here’s what they actually do:
- Define tagging standards: Tag policies specify which tag keys and values are allowed for resources
- Enable compliance reporting: They’re used by AWS Resource Groups Tagging API and Cost Allocation to identify resources that don’t match the policy
- Don’t prevent resource creation: Unlike SCPs, tag policies never block resource creation, even if a resource lacks required tags
Many people confuse tag policies with SCPs, expecting them to prevent non-compliant tagging. They don’t. To prevent non-compliant resource creation, you need SCPs with tag-based conditions.
The Fix
Option 1: Use SCPs to Enforce Required Tags (Preventive)
To actually prevent resource creation without required tags, use an SCP with tag conditions:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Deny",
"Action": [
"ec2:RunInstances"
],
"Resource": "arn:aws:ec2:*:*:instance/*",
"Condition": {
"Null": {
"aws:RequestTag/Environment": "true"
}
}
},
{
"Effect": "Deny",
"Action": [
"s3:CreateBucket"
],
"Resource": "*",
"Condition": {
"Null": {
"aws:RequestTag/CostCenter": "true"
}
}
}
]
}
This SCP denies EC2 instance creation unless an Environment tag is provided, and denies S3 bucket creation unless a CostCenter tag is provided.
Create and attach this SCP:
# Create the SCP
aws organizations create-policy \
--name RequiredTagsPolicy \
--description "Enforce required tags on resource creation" \
--type SERVICE_CONTROL_POLICY \
--content file://required-tags-scp.json
# Attach it to an OU
aws organizations attach-policy \
--policy-id p-0123456789abcdef0 \
--target-id ou-xxxx-yyyyyyyy
Option 2: Use Tag Policies for Compliance Reporting
If you want tag policies for standardization and reporting (not prevention), define the policy:
{
"tags": {
"Environment": {
"tag_key": {
"@@assign": ["Environment"]
},
"enforced_for": {
"@@assign": ["ec2:*", "rds:*"]
},
"tag_value": {
"@@assign": ["prod", "staging", "dev"]
}
},
"CostCenter": {
"tag_key": {
"@@assign": ["CostCenter"]
},
"enforced_for": {
"@@assign": ["ec2:*", "s3:*"]
}
}
}
}
Create and activate the tag policy:
# Create the tag policy
aws organizations create-policy \
--name StandardTagsPolicy \
--description "Define standard tags for compliance reporting" \
--type TAG_POLICY \
--content file://tag-policy.json
# Attach it to an OU
aws organizations attach-policy \
--policy-id p-0123456789abcdef0 \
--target-id ou-xxxx-yyyyyyyy
Step 3: Check Compliance with Resource Groups Tagging API
Once a tag policy is attached, use the Resource Groups Tagging API to find non-compliant resources:
# Get non-compliant resources for an OU
aws resourcegroupstaggingapi get-compliance-summary \
--target-id ou-xxxx-yyyyyyyy \
--query 'ComplianceSummary.ComplianceByResource[*]'
# Get details of resources that don't match the tag policy
aws resourcegroupstaggingapi get-resources \
--resource-type-filters ec2:instance \
--tag-filter Key=Environment,Values=prod,staging,dev \
--region us-east-1 \
--query 'ResourceTagMappingList[?!(Tags[?Key==`Environment`])].{ResourceId:ResourceARN}'
This shows resources missing the Environment tag.
Step 4: Enforce Tags via AWS Config Rules (Preventive Alternative)
For preventive enforcement without SCPs, use AWS Config rules:
# Create a Config rule to check for required tags
aws configservice put-config-rule \
--config-rule '{
"ConfigRuleName": "required-tags",
"Source": {
"Owner": "AWS",
"SourceIdentifier": "REQUIRED_TAGS"
},
"Scope": {
"ComplianceResourceTypes": ["AWS::EC2::Instance", "AWS::S3::Bucket"]
},
"InputParameters": "{\"tag1Key\": \"Environment\", \"tag2Key\": \"CostCenter\"}"
}'
# Check compliance
aws configservice describe-compliance_by_config_rule \
--query 'ComplianceByConfigRules[0]'
Config will mark non-compliant resources but won’t prevent creation (unlike SCPs).
How to Run This
- For preventive enforcement: Create an SCP with
aws:RequestTagconditions that deny resource creation without required tags - For compliance reporting: Create a tag policy that defines allowed tags, then query with
resourcegroupstaggingapi - Optional: Add AWS Config rules for post-creation compliance checks
- Attach policies to OUs and test with a development OU first
- Educate teams that tag policies report non-compliance; SCPs prevent it
Is This Safe?
Completely safe. SCPs with tag conditions are safe and widely used. Tag policies are purely informational.
Key Takeaway
Tag policies are compliance reporting tools, not preventive controls. Use SCPs with tag-based conditions to actually prevent non-compliant resource creation. Tag policies should be used in conjunction with SCPs and Config rules for comprehensive tagging governance.