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

  1. For preventive enforcement: Create an SCP with aws:RequestTag conditions that deny resource creation without required tags
  2. For compliance reporting: Create a tag policy that defines allowed tags, then query with resourcegroupstaggingapi
  3. Optional: Add AWS Config rules for post-creation compliance checks
  4. Attach policies to OUs and test with a development OU first
  5. 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.


Have questions? Connect with me on LinkedIn or X.