Why it matters
Without tags, you can’t answer basic questions like “How much does Team X cost?” or “What’s our spend on Project Y?” Tags let you slice cost data by team, project, environment, and more.
Cost Explorer and AWS Budgets rely on tags to show detailed breakdowns. Without them, you’re looking at one giant bill with no accountability.
1. Define the tags to be used
Define these core tags and enforce them on all resources:
Environment- production, staging, development, testTeamorOwner- Which team owns this resourceProjectorApplication- Which project/applicationCostCenter- For chargeback to business unitsName- Human-readable identifier
Additional useful tags:
ManagedBy- Terraform, CloudFormation, manualExpirationDate- For temporary resourcesDataClassification- public, internal, confidential
2. Enable cost allocation tags
Before tags show up in Cost Explorer or billing reports, you must activate them in the Billing Console:
- Go to AWS Billing Console → Cost Allocation Tags
- Find your tag keys (e.g.,
Environment,Team,Project) - Click Activate
- Wait 24 hours for tags to appear in reports
Important: Tags won’t appear in billing data until you activate them. If you skip this step, all your tagging work is invisible to cost reporting.
For AWS Organizations: This must be done from the management account. Member accounts cannot activate cost allocation tags.
3. Enforce the tags
AWS offers 2 tools to enforce tags: Organizations Tag Policies and Service Control Policies.
Key differences:
- Tag Policies: Validate tag values but not presence. They’ll block “prod” when you require “prd”, but won’t stop untagged resources. Applies to creation and updates.
- Service Control Policies: Can enforce tag presence and block resource creation that doesn’t meet tagging requirements.
Both tag policies and service control policies are managed from the AWS Organizations console.
Which one should you use?
To enforce tag presence, you MUST use SCPs. You can either:
- Use SCPs alongside Tag Policies (SCPs for presence, Tag Policies for values), or
- Use SCPs alone to enforce both presence and values
Examples
Tag Policy:
{
"tags": {
"Environment": {
"tag_key": {
"@@assign": "Environment",
"@@operators_allowed_for_child_policies": ["@@none"]
},
"tag_value": {
"@@assign": [
"Prod",
"Non-prod"
],
"@@operators_allowed_for_child_policies": ["@@none"]
},
"enforced_for": {
"@@assign": [
"ec2:ALL_SUPPORTED",
"lambda:function"
]
}
}
}
}
This policy ensures that if the Environment tag is present on EC2 instances or Lambda functions, its value must be either Prod or Non-prod.
SCPs:
This SCP blocks any attempt to create a Lambda function if the Environment tag is missing. The Null condition checks for the absence of aws:RequestTag/Environment, and when it’s true, the lambda:CreateFunction call is denied at the OU/Account level. Note that this only enforces the presence of the Environment tag, not which values are allowed.
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "DenyLambdaWithoutEnvironmentTag",
"Effect": "Deny",
"Action": [
"lambda:CreateFunction"
],
"Resource": "*",
"Condition": {
"Null": {
"aws:RequestTag/Environment": "true"
}
}
}
]
}
This SCP enforces that new Lambda functions use one of a specific set of Environment values (Prod, Dev, Staging). If Environment is set to anything else, the StringNotEquals condition matches and the lambda:CreateFunction call is denied.
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "DenyLambdaWithoutValidTags",
"Effect": "Deny",
"Action": "lambda:CreateFunction",
"Resource": "*",
"Condition": {
"StringNotEquals": {
"aws:RequestTag/Environment": [
"Prod",
"Dev",
"Staging"
]
}
}
}
]
}
4. Tag Existing resources with the Tag Editor (Optional)
You can bulk tag existing resources on AWS Resource Explorer. If your resources were created with IaC you should add those tags in your code instead of adding them manually
- Search the resources in the region you want to tag
- Select them, click on Actions and then Manage tags
- Add the tags you want and apply
AWS Config for tagging compliance
AWS Config monitors your resources and evaluates them against rules whenever they change. The required-tags managed rule checks for up to 6 mandatory tags and their values each time a resource is created or updated.
Monitoring Tag Changes with EventBridge
EventBridge can stream tagging events from AWS services in real time. To monitor tag changes, you can create a rule that targets the aws.tag event source:
{
"detail-type": ["Tag Change on Resource"],
"source": ["aws.tag"],
"detail": {
"tags": {
"Application": [{
"exists": true
}]
}
}
}
This rule listens for tag changes on any resource that has the Application tag. You can send these events to a Lambda function (or another target) to validate tags against your own rules, which gives you more flexibility than relying only on AWS Config managed rules.
Best practices
- Use consistent naming - Pick lowercase-hyphen-case or CamelCase and stick to it
- Automate tagging - Default tags in IaC prevent human error
- Keep values simple - Avoid spaces and special characters
- Document everything - Teams need to know what tags to use
- Audit regularly - Use Cost Explorer and AWS Config to spot untagged spend
- Review “No tag” costs - If a large portion is untagged, prioritize backfilling
Common pitfalls
- Inconsistent values - “prod” vs “production” vs “Production” create separate categories
- Too many tags - Start with 3-5 core tags, add more later if needed
- No enforcement - Without automation or policies, tagging becomes optional
- Forgetting shared resources - VPCs, subnets, and shared buckets need tags too