Links#
https://docs.aws.amazon.com/whitepapers/latest/tagging-best-practices/tagging-best-practices.html
https://docs.aws.amazon.com/tag-editor/latest/userguide/best-practices-and-strats.html
https://docs.aws.amazon.com/awsaccountbilling/latest/aboutv2/cost-alloc-tags.html
https://docs.aws.amazon.com/organizations/latest/userguide/orgs_manage_policies_tag-policies.html
https://docs.aws.amazon.com/IAM/latest/UserGuide/access_tags.html
https://github.com/hashicorp/terraform-provider-aws/blob/main/website/docs/index.html.markdown1. Important Points#
AWS tag 的真实用途是资源检索、成本分摊、责任归属、自动化和 ABAC。Name 只适合 human readable,不能替代 tags。
tag design principles:
use consistent tag keys and allowed values
do not put secret / customer PII / ticket detail in tags
use tags for filter, cost allocation, ownership, automation, and audit
use AWS Organizations tag policies when tags must be enforced at org scale
use IAM condition keys / ABAC only after tag governance is stableTag key style:
AWS org-scale recommendation:
use lowercase hyphen keys with organization prefix
example:
acme:project
acme:environment
acme:managed-by
common small-team style:
use PascalCase keys
example:
Project
Environment
ManagedByPick one style and keep it consistent. This repository already uses Project / Environment / ManagedBy in several Terraform examples, so the examples below use PascalCase. If your AWS Organization has a formal tag policy, prefer the organization standard.
2. Tag Levels#
Minimum tags:
| Tag | Example | Why |
|---|---|---|
Project |
order |
product / workload boundary |
Environment |
prod |
dev / uat / prod separation |
ManagedBy |
terraform |
ownership by automation |
Recommended operational tags:
| Tag | Example | Why |
|---|---|---|
Region |
ap-east-1 |
cost/CMDB/audit filters across regions |
Stack |
ecs |
Terraform root module or platform layer |
Service |
api |
service ownership and alarm/dashboard grouping |
Owner |
platform |
accountable owner |
Team |
platform |
team routing |
Cost and governance tags:
| Tag | Example | Why |
|---|---|---|
CostCenter |
cc-1234 |
cost allocation reports |
Criticality |
high |
incident priority / backup / DR policy |
DataClass |
internal |
data handling policy |
Compliance |
pci |
compliance scope |
3. Region And Stack#
Region and Stack are useful, but not mandatory.
Region is useful when:
cost reports need tag-based regional filtering
CMDB / inventory tools ingest tags but not AWS region metadata
multi-region incident review needs resource context quickly
Region can be skipped when:
account and tooling already make region obvious
adding it creates noisy duplication without reporting valueStack is useful when:
one environment has multiple Terraform root modules
examples:
monitoring
sns
vpc
ecs
rds
resource owner needs to know which Terraform state manages it
cost allocation needs platform layer split, for example network vs database vs alerting
Stack can be skipped when:
one root module manages only one obvious stack
Terraform state / naming / repository layout already answers ownership clearlyPractical default:
small baseline:
Project
Environment
ManagedBy
good default:
Project
Environment
Region
Stack
ManagedBy
organization / production:
Project
Environment
Region
Stack
Service
Owner
Team
CostCenter
Criticality
ManagedBy4. Terraform Pattern#
Put stable common tags in provider.default_tags. Put resource-specific tags on resources or modules.
locals {
project = "order"
environment = "prod"
region = "ap-east-1"
stack = "ecs"
service = "api"
common_tags = {
Project = local.project
Environment = local.environment
Region = local.region
Stack = local.stack
Service = local.service
ManagedBy = "terraform"
Owner = "alice.chen@example.com"
Team = "checkout-platform"
CostCenter = "fin-ops-042"
Criticality = "high"
}
}
provider "aws" {
region = local.region
default_tags {
tags = local.common_tags
}
}Resource-specific tags:
resource "aws_subnet" "private_app" {
for_each = local.private_app_subnets
vpc_id = aws_vpc.main.id
cidr_block = each.value.cidr
availability_zone = each.value.az
tags = {
Name = "${local.project}-${local.environment}-private-app-${each.key}-subnet"
NetworkTier = "private"
SubnetRole = "private-app"
RouteScope = "nat"
}
}
resource "aws_cloudwatch_metric_alarm" "lambda_errors" {
alarm_name = "${local.project}-${local.environment}-${local.service}-lambda-errors-high"
tags = {
Name = "${local.project}-${local.environment}-${local.service}-lambda-errors-high"
Severity = "p1"
AlarmSignal = "errors-high"
MetricNamespace = "AWS/Lambda"
RunbookUrl = "https://docs.example.com/runbooks/lambda-errors"
DashboardUrl = "https://console.aws.amazon.com/cloudwatch/"
}
}5. Resource-Specific Tags#
Use these only where they add real filter value.
| Resource Area | Extra Tags | Example Values |
|---|---|---|
| VPC / subnet | NetworkTier, SubnetRole, RouteScope |
private, private-app, nat |
| ALB / target group | Exposure, Protocol, Service |
public, https, api |
| ECS / Lambda | Runtime, Service, Criticality |
nodejs22, api, high |
| RDS / cache | DataClass, BackupPolicy, Criticality |
confidential, daily-35d, high |
| SQS / SNS | MessageClass, Producer, Consumer |
event, api, worker |
| CloudWatch alarm | Severity, AlarmSignal, RunbookUrl, DashboardUrl |
p1, 5xx-high, URL |
| IAM | AccessScope, PrincipalType |
deploy, ci |
Do not tag everything with every possible key. A tag should answer one of these questions:
who owns it?
what project/service/env is it for?
which Terraform stack manages it?
how is cost allocated?
how should automation select it?
how should incident response route it?6. Cost / ABAC / Tag Policy#
Cost allocation:
activate cost allocation tags in Billing
CostCenter / Project / Environment / Service are common report dimensions
tags only help cost reports after activation and ingestion delayABAC:
use tags in IAM conditions only after tag quality is enforced
examples:
allow operations only when aws:ResourceTag/Environment = prod
allow creation only when aws:RequestTag/Project is present
risk:
inconsistent tags can become inconsistent access controlTag policies:
use AWS Organizations tag policies to standardize keys and allowed values
tag policies help detect/enforce casing and values
they do not replace IAM permissions7. Checklist#
baseline:
Project
Environment
ManagedBy
recommended:
Region
Stack
Service
Owner / Team
when needed:
CostCenter
Criticality
DataClass
Compliance
RunbookUrl / DashboardUrl for alarms
avoid:
secrets or customer PII in tags
mixing Project and project as different keys
changing tag key names after cost reports and IAM policies depend on them
relying on Name for Terraform data filters when a structured tag exists