An AWS Tagging Strategy for Cost Allocation
Introduction¶
Cost allocation, automation, and access control on AWS all depend on consistent resource tags. Retrofitting tags across thousands of resources is miserable, so design the schema early and enforce it.
A minimal schema¶
Fewer, mandatory tags beat many optional ones. A common core:
| Tag key | Example | Purpose |
|---|---|---|
Environment |
prod / staging / dev |
filter spend and access by env |
Team (or CostCenter) |
payments |
who owns and pays for it |
Service (or Application) |
checkout-api |
roll up spend per service |
ManagedBy |
terraform / console |
find click-ops resources |
Owner |
payments-oncall@ |
who to ask |
Decide case and allowed values up front (prod not Prod/production) —
tag values are case-sensitive and Cost Explorer treats Prod and prod as
different.
Activate cost allocation tags¶
Tags don't appear in cost reports until activated:
- Billing console → Cost allocation tags → activate each user-defined tag key. Takes ~24h to start populating, and is not retroactive.
- AWS-generated tags (
aws:createdBy,aws:cloudformation:stack-name) can be activated too.
Enforce¶
- Tag Policies (Organizations) — define allowed keys/values and report non-compliance. Note: tag policies report drift; they don't block by default.
- SCPs — actually deny creation without required tags:
{
"Effect": "Deny",
"Action": ["ec2:RunInstances", "rds:CreateDBInstance"],
"Resource": "*",
"Condition": {
"Null": { "aws:RequestTag/Team": "true" }
}
}
- IaC — set default tags at the provider level so every resource inherits them:
provider "aws" {
default_tags {
tags = {
Environment = "prod"
Team = "payments"
ManagedBy = "terraform"
}
}
}
- AWS Config rule
required-tags— flags non-compliant existing resources; pair with an auto-remediation or a nagging report.
Costs tags can't reach¶
- Untaggable resources — data transfer, some support charges, a few older service dimensions. These land in "untagged".
- Shared costs — a NAT Gateway, an ALB, a shared EKS cluster serving many
teams. Options: a
Sharedteam tag and split by an agreed formula in your FinOps tooling, or split by usage (per-namespace with Kubecost/OpenCost for EKS). - Reserved Instances / Savings Plans discounts — apply at the billing level; amortised cost views in Cost Explorer attribute them back.
Reporting¶
- Cost Explorer — group by tag, filter by tag, set up per-team reports. Good for exploration and monthly reviews.
- Cost and Usage Report (CUR) — the raw hourly line-item data to S3, queried with Athena or loaded into QuickSight / a BI tool. The source of truth for chargeback.
- Budgets — per-tag budget alerts (
Team=paymentsmonthly budget with alerts at 80/100/120%).
Verification and troubleshooting¶
# Find untagged resources
aws resourcegroupstaggingapi get-resources \
--tag-filters Key=Team --query 'ResourceTagMappingList[?length(Tags)==`0`]'
# Bulk-tag from a list
aws resourcegroupstaggingapi tag-resources \
--resource-arn-list arn:... --tags Team=payments,Environment=prod
- Tag added but not in Cost Explorer — not activated as a cost allocation tag, or it's been < 24h, or the spend predates activation (not retroactive).
- Big "untagged" or "No Team" bucket — resources created before enforcement, untaggable line items, or an automation creating resources without default tags. Backfill with Tag Editor / resourcegroupstaggingapi; add provider default_tags.
- Same logical value split across rows — case/spelling drift (
prodvsProduction). Normalise with a tag policy and a one-time bulk re-tag. - SCP blocking legitimate creation — a service that tags resources
after creation (some do) fails the
RequestTagcondition. Useaws:ResourceTagin a Config rule instead of an SCP for those, or exempt the service role. - CUR and Cost Explorer disagree — different rounding, amortisation settings, or time granularity. CUR line items are authoritative; match the Cost Explorer settings (amortised vs unblended).
- Auto Scaling / spot instances untagged — set
PropagateAtLaunchon ASG tags, or tags in the launch template.
Related tools and reading¶
- On-site: JSON formatter.
- Related posts: AWS IAM policy evaluation logic, Terraform modules and remote state.
Stuck on something this site can't fix?Reach out to Prabath directly on LinkedIn.More in Cloud & AWS
AWS WAF: The Rules That Matter
AWS WAF filters HTTP requests at the ALB/CloudFront/API Gateway edge. Here's a sensible baseline Web ACL, how to tune with count mode, and the false-positive traps.
October 24, 2026An S3 Static Site Behind CloudFront
The right way to host a static site on AWS: a private S3 bucket, CloudFront with OAC in front, HTTPS via ACM, and SPA fallback routing. Plus the 403s that trip everyone.
October 24, 2026CloudWatch Logs and Metric Filters
CloudWatch Logs is cheap to write and expensive to forget about. Here's retention, turning error patterns into metrics and alarms, and Logs Insights for ad-hoc queries.
October 24, 2026