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/IAM/latest/UserGuide/id_tags.html
https://developer.hashicorp.com/terraform/language/style
https://github.com/cloudposse/terraform-null-label
https://docs.aws.amazon.com/cli/latest/reference/cloudwatch/put-metric-alarm.html
https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/Create_Composite_Alarm.html
https://docs.aws.amazon.com/AWSCloudFormation/latest/TemplateReference/aws-resource-elasticloadbalancingv2-targetgroup.html
https://docs.aws.amazon.com/lambda/latest/dg/configuration-vpc.html
https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-security-groups.html
https://docs.aws.amazon.com/AmazonS3/latest/userguide/bucketnamingrules.html1. Source Boundary#
AWS 没有一套覆盖所有 service 的官方 resource name pattern。官方最佳实践重点是:
AWS:
use a consistent tagging strategy
do not put sensitive data in tags or names
use tags for search, filter, automation, cost, and access control
a structured resource name is useful, but name can only hold limited metadata
Terraform:
resource local name should be descriptive noun
use underscores in Terraform resource identifiers
do not repeat resource type in Terraform local name
Terraform ecosystem:
common label pattern is namespace/environment/stage/name/attributes
generated Name tag is usually a compact human-readable idThis page therefore uses a conservative Terraform/AWS naming standard:
AWS resource Name / name:
<namespace>-<stage>-<name>-<attributes>
example:
acme-prod-api-lambda-sg
meaning:
namespace:
org / platform short name, for example acme
stage:
dev / uat / prod
name:
application or component, for example api / worker / orders
attributes:
role / tier / protocol / resource type, for example private sgThis follows the widely used Terraform label convention from Cloud Posse, while keeping AWS official guidance: names stay short and readable; tags carry full metadata.
2. Region In Name#
Default rule:
put Region in tag, not nameAdd region to name only when the name will be read outside clear AWS regional context.
| Add Region To Name When | Reason | Example |
|---|---|---|
| resource name must be globally unique | avoid cross-region/account collision | acme-prod-assets-ap-east-1 |
| global resource manages regional workload | global list does not show workload region clearly | acme-prod-api-ap-east-1-deploy-role |
| same service is deployed to multiple regions and alerts are centralized | PagerDuty / Slack / email may show only alarm name | acme-prod-api-ap-east-1-lambda-errors-high |
| CMDB / inventory / audit tool shows name but not AWS region | operators need region without opening AWS console | acme-prod-alerts-ap-east-1 |
| Terraform state key stores multi-region workloads | state ownership must be explicit | order/prod/default/ap-east-1/ecs/terraform.tfstate |
Usually no region in name:
VPC / subnet / route table / security group:
already regional and VPC-scoped
ALB / NLB / target group:
regional; add region only when centralized alert/inventory tools lose region context
ECS / Lambda / RDS / ElastiCache / DynamoDB / SQS:
regional; tag Region is normally enoughPractical rule:
name:
<namespace>-<stage>-<service>-<attributes>
tag:
Region = ap-east-1
multi-region name when needed:
<namespace>-<stage>-<service>-<region>-<attributes>3. Tags First#
Use tags for filtering, cost allocation, ownership, automation, audit, and ABAC. Do not parse important metadata from names.
Full tag design is covered in Terraform AWS Tags. This naming page only keeps the rule that affects names:
Name:
human-readable resource label
tags:
structured metadata for filters, cost, ownership, routing, and policyMinimum tags:
Project
Environment
ManagedByGood default:
Project
Environment
Region
Stack
Service
ManagedByProduction / organization tags:
Project
Environment
Region
Stack
Service
Owner
Team
CostCenter
Criticality
ManagedByTerraform pattern:
locals {
namespace = "acme"
stage = "prod"
name = "api"
region = "ap-east-1"
stack = "ecs"
id = "${local.namespace}-${local.stage}-${local.name}"
common_tags = {
Project = "order"
Environment = local.stage
Region = local.region
Stack = local.stack
Service = local.name
ManagedBy = "terraform"
Owner = "alice.chen@example.com"
Team = "checkout-platform"
CostCenter = "fin-ops-042"
Criticality = "high"
}
}Keep Name for console readability, but do not depend on Name for Terraform data filters when a structured tag exists.
4. Terraform Local Names#
Terraform identifiers are not AWS resource names.
| Terraform Resource | Good | Avoid |
|---|---|---|
| security group | aws_security_group.lambda |
aws_security_group.acme_prod_api_lambda_sg |
| EC2 security group | aws_security_group.ec2 |
aws_security_group.api_ec2_security_group |
| subnet map | aws_subnet.private_app |
aws_subnet.private_subnet_1 |
| target group | aws_lb_target_group.blue |
aws_lb_target_group.tg1 |
| IAM role | aws_iam_role.task |
aws_iam_role.api_task_role |
resource "aws_security_group" "lambda" {
name = "${local.id}-lambda-sg"
tags = merge(local.tags, {
Name = "${local.id}-lambda-sg"
})
}5. Network#
| Resource | AWS Name / Name Tag | Example |
|---|---|---|
| VPC | <namespace>-<stage>-<network>-vpc |
acme-prod-core-vpc |
| public subnet | <namespace>-<stage>-<network>-public-<az>-subnet |
acme-prod-core-public-a-subnet |
| private app subnet | <namespace>-<stage>-<network>-private-app-<az>-subnet |
acme-prod-core-private-app-a-subnet |
| private data subnet | <namespace>-<stage>-<network>-private-data-<az>-subnet |
acme-prod-core-private-data-a-subnet |
| route table | <namespace>-<stage>-<network>-<route-scope>-<az>-rt |
acme-prod-core-nat-a-rt |
| internet gateway | <namespace>-<stage>-<network>-igw |
acme-prod-core-igw |
| NAT gateway | <namespace>-<stage>-<network>-<az>-nat |
acme-prod-core-a-nat |
| NAT EIP | <namespace>-<stage>-<network>-<az>-nat-eip |
acme-prod-core-a-nat-eip |
| VPC endpoint | <namespace>-<stage>-<service>-vpce |
acme-prod-s3-vpce |
| endpoint SG | <namespace>-<stage>-vpce-sg |
acme-prod-vpce-sg |
Use subnet tags for Terraform filters:
acme:network-tier:
public / private
acme:subnet-role:
public-ingress / private-app / private-data / private-endpoint6. Security Groups#
Security group names should describe the resource they protect, not only the protocol.
| Use Case | AWS Name | Example |
|---|---|---|
| Lambda function SG | <namespace>-<stage>-<function>-lambda-sg |
acme-prod-order-api-lambda-sg |
| EC2 instance SG | <namespace>-<stage>-<role>-ec2-sg |
acme-prod-bastion-ec2-sg |
| ECS task SG | <namespace>-<stage>-<service>-task-sg |
acme-prod-api-task-sg |
| ALB SG | <namespace>-<stage>-<service>-alb-sg |
acme-prod-api-alb-sg |
| RDS SG | <namespace>-<stage>-<db>-rds-sg |
acme-prod-orders-rds-sg |
| ElastiCache SG | <namespace>-<stage>-<cache>-redis-sg |
acme-prod-session-redis-sg |
| VPC endpoint SG | <namespace>-<stage>-vpce-sg |
acme-prod-vpce-sg |
Examples:
resource "aws_security_group" "lambda" {
name = "${local.id}-lambda-sg"
description = "Security group for ${local.id} Lambda ENIs"
vpc_id = data.aws_vpc.selected.id
tags = merge(local.tags, {
Name = "${local.id}-lambda-sg"
})
}
resource "aws_security_group" "ec2" {
name = "${local.namespace}-${local.stage}-bastion-ec2-sg"
description = "Security group for bastion EC2 instances"
vpc_id = data.aws_vpc.selected.id
tags = merge(local.tags, {
Name = "${local.namespace}-${local.stage}-bastion-ec2-sg"
})
}7. Load Balancing#
Target group names have a hard 32-character limit and allow only alphanumeric characters or hyphens. Use a short name.
| Resource | AWS Name | Example |
|---|---|---|
| public ALB | <namespace>-<stage>-<service>-public-alb |
acme-prod-api-public-alb |
| internal ALB | <namespace>-<stage>-<service>-internal-alb |
acme-prod-api-internal-alb |
| NLB | <namespace>-<stage>-<service>-nlb |
acme-prod-ingress-nlb |
| target group | <ns>-<stg>-<svc>-<port>-tg |
ac-p-api-8080-tg |
| blue target group | <ns>-<stg>-<svc>-blue-tg |
ac-p-api-blue-tg |
| green target group | <ns>-<stg>-<svc>-green-tg |
ac-p-api-green-tg |
8. Compute#
| Resource | AWS Name / Name Tag | Example |
|---|---|---|
| Lambda function | <namespace>-<stage>-<function> |
acme-prod-order-capture |
| Lambda alias | <stage> |
prod |
| Lambda layer | <namespace>-<stage>-<runtime>-<purpose>-layer |
acme-prod-node-common-layer |
| ECS cluster | <namespace>-<stage>-ecs |
acme-prod-ecs |
| ECS service | <namespace>-<stage>-<service> |
acme-prod-api |
| ECS task family | <namespace>-<stage>-<service> |
acme-prod-api |
| ECR repository | <namespace>/<service> |
acme/api |
| EC2 instance Name tag | <namespace>-<stage>-<role>-<index> |
acme-prod-bastion-01 |
| launch template | <namespace>-<stage>-<role>-lt |
acme-prod-worker-lt |
| autoscaling group | <namespace>-<stage>-<role>-asg |
acme-prod-worker-asg |
| CodeBuild project | <namespace>-<stage>-<service>-build |
acme-prod-api-build |
| Step Functions state machine | <namespace>-<stage>-<workflow> |
acme-prod-order-submit |
| API Gateway stage | <stage> |
prod |
Do not always append lambda to Lambda function names. Lambda ARNs already include the service type; use lambda in related resources such as IAM role or SG when it prevents ambiguity.
9. Data#
| Resource | AWS Name | Example |
|---|---|---|
| S3 app bucket | <namespace>-<stage>-<purpose>-<region> |
acme-prod-assets-ap-east-1 |
| S3 log bucket | <namespace>-<stage>-logs-<region> |
acme-prod-logs-ap-east-1 |
| DynamoDB table | <namespace>-<stage>-<entity> |
acme-prod-orders |
| RDS instance | <namespace>-<stage>-<db> |
acme-prod-orders |
| RDS cluster | <namespace>-<stage>-<db>-cluster |
acme-prod-orders-cluster |
| RDS subnet group | <namespace>-<stage>-<db>-subnet-group |
acme-prod-orders-subnet-group |
| RDS parameter group | <namespace>-<stage>-<engine>-<major>-pg |
acme-prod-postgres-16-pg |
| ElastiCache replication group | <namespace>-<stage>-<cache> |
acme-prod-session |
| OpenSearch domain | <namespace>-<stage>-<domain> |
acme-prod-search |
| EFS Name tag | <namespace>-<stage>-<purpose>-efs |
acme-prod-shared-efs |
| AWS Backup vault | <namespace>-<stage>-backup-vault |
acme-prod-backup-vault |
S3 bucket names are globally unique within an AWS partition. Add account ID or region when collision risk matters.
10. Messaging#
| Resource | AWS Name | Example |
|---|---|---|
| SQS standard queue | <namespace>-<stage>-<queue> |
acme-prod-payment |
| SQS FIFO queue | <namespace>-<stage>-<queue>.fifo |
acme-prod-payment.fifo |
| SQS DLQ | <namespace>-<stage>-<queue>-dlq |
acme-prod-payment-dlq |
| SNS topic | <namespace>-<stage>-<event> |
acme-prod-order-created |
| EventBridge bus | <namespace>-<stage>-bus |
acme-prod-bus |
| EventBridge rule | <namespace>-<stage>-<event>-rule |
acme-prod-order-created-rule |
| EventBridge Scheduler | <namespace>-<stage>-<job>-schedule |
acme-prod-reconcile-schedule |
| Kinesis stream | <namespace>-<stage>-<stream> |
acme-prod-clicks |
| Firehose stream | <namespace>-<stage>-<destination>-firehose |
acme-prod-s3-firehose |
Avoid redundant suffixes when the AWS service already scopes the object type and the name is clear. Use -dlq, -rule, -schedule, or -firehose when the suffix prevents ambiguity.
11. IAM#
| Resource | AWS Name | Example |
|---|---|---|
| app IAM role | <namespace>-<stage>-<service>-role |
acme-prod-api-role |
| Lambda IAM role | <namespace>-<stage>-<function>-lambda-role |
acme-prod-order-capture-lambda-role |
| ECS task role | <namespace>-<stage>-<service>-task-role |
acme-prod-api-task-role |
| ECS execution role | <namespace>-<stage>-<service>-exec-role |
acme-prod-api-exec-role |
| EC2 instance role | <namespace>-<stage>-<role>-ec2-role |
acme-prod-worker-ec2-role |
| IAM policy | <namespace>-<stage>-<subject>-<permission>-policy |
acme-prod-api-s3-read-policy |
| instance profile | <namespace>-<stage>-<role>-profile |
acme-prod-worker-profile |
| CI deploy role | <namespace>-<stage>-ci-deploy-role |
acme-prod-ci-deploy-role |
| Terraform provision role | <namespace>-<stage>-terraform-provision-role |
acme-prod-terraform-provision-role |
| Terraform state role | <namespace>-<stage>-terraform-state-role |
acme-prod-terraform-state-role |
IAM is global inside an account, so include stage or account role in names unless each account maps to exactly one environment.
12. Observability#
| Resource | AWS Name | Example |
|---|---|---|
| Lambda log group | /aws/lambda/<function-name> |
/aws/lambda/acme-prod-order-capture |
| ECS log group | /aws/ecs/<namespace>/<stage>/<service> |
/aws/ecs/acme/prod/api |
| app log group | /app/<namespace>/<stage>/<service> |
/app/acme/prod/api |
| CloudWatch dashboard | <namespace>-<stage>-dashboard |
acme-prod-dashboard |
| metric filter | <namespace>-<stage>-<service>-<metric>-filter |
acme-prod-api-error-filter |
CloudWatch alarm#
CloudWatch alarm name is unique within one Region. AWS allows UTF-8 names up to 255 characters and disallows ASCII control characters. AWS does not publish a universal alarm naming pattern, so use the same platform naming standard as other Terraform AWS resources and keep full metadata in tags.
Use the same Cloud Posse style label order as other AWS resources. Keep severity in tags by default, because severity often changes with threshold, routing, or business impact while the monitored resource identity stays the same.
metric alarm:
<namespace>-<stage>-<service>-<resource-type>-<signal>
composite alarm:
<namespace>-<stage>-<service>-composite-<symptom>
log metric alarm:
<namespace>-<stage>-<service>-log-<signal>
optional severity suffix:
<namespace>-<stage>-<service>-<resource-type>-<signal>-<severity>Do not confuse these two meanings of namespace:
organization namespace:
acme
appears in alarm_name to match the rest of the naming standard
CloudWatch metric namespace:
AWS/Lambda
AWS/EC2
AWS/ApplicationELB
custom namespace such as Acme/OrderApi
belongs in the alarm metric definition and tags, not usually in alarm_nameWhy this pattern:
| Decision | Reason | Source |
|---|---|---|
start with namespace-stage-service |
follows Cloud Posse label order and matches the rest of this page | Cloud Posse label convention |
include stage |
alarms are operational objects; responders must know prod/uat/dev immediately | AWS tagging guidance recommends environment metadata |
include detailed service |
maps alarm to ownership, runbook, dashboard, and incident routing; include resource identity here when needed | AWS tagging guidance recommends application/team metadata |
include resource-type |
same service can have ALB, ECS, Lambda, RDS, SQS alarms with the same signal name | operational search/readability |
include signal and direction |
alarm title should say what is wrong without opening the alarm details | operational search/readability |
keep severity in tags by default |
severity can change without renaming the alarm; routing can read tags or alarm actions | AWS tagging guidance + operational stability |
| keep CloudWatch metric namespace out of name | AWS/Lambda is already in the alarm metric definition; putting / in names is noisy for incident tools |
CloudWatch alarm API has separate namespace field |
This is not an AWS-mandated alarm naming standard. It is the repository’s recommended convention built from AWS constraints and widely used Terraform naming practice.
Token rules:
| Token | Example | Rule |
|---|---|---|
namespace |
acme |
organization / platform namespace, not CloudWatch metric namespace |
stage |
prod, uat, dev |
same value as environment tag |
service |
api, api-public, api-blue, payment-dlq |
user-facing service or component; make it specific enough to avoid a separate resource token |
resource-type |
alb, tg, ecs, lambda, rds, sqs, ec2 |
short AWS resource type |
signal |
5xx-high, cpu-high, oldest-message-age-high |
symptom and direction |
severity |
p0, p1, p2, p3 |
tag by default; optional suffix only when notification titles need it |
Examples:
| Alarm Type | Pattern Example |
|---|---|
| ALB 5xx | acme-prod-api-public-alb-5xx-high |
| Target group unhealthy | acme-prod-api-blue-tg-healthy-host-low |
| ECS task count | acme-prod-api-ecs-service-task-below-desired |
| Lambda errors | acme-prod-payment-lambda-capture-errors-high |
| Lambda throttles | acme-prod-payment-lambda-capture-throttles-high |
| EC2 CPU | acme-prod-bastion-ec2-01-cpu-high |
| RDS CPU | acme-prod-orders-rds-main-cpu-high |
| RDS free storage | acme-prod-orders-rds-main-free-storage-low |
| SQS backlog | acme-prod-payment-sqs-main-oldest-message-age-high |
| DynamoDB throttles | acme-prod-orders-ddb-throttles-high |
| log error metric | acme-prod-api-log-error-rate-high |
| composite service unavailable | acme-prod-api-composite-service-unavailable |
| optional severity suffix | acme-prod-api-public-alb-5xx-high-p1 |
Terraform example:
resource "aws_cloudwatch_metric_alarm" "lambda_errors" {
alarm_name = "${local.namespace}-${local.stage}-${local.name}-lambda-capture-errors-high"
alarm_description = "Lambda capture errors are above threshold"
comparison_operator = "GreaterThanOrEqualToThreshold"
evaluation_periods = 3
datapoints_to_alarm = 2
metric_name = "Errors"
namespace = "AWS/Lambda"
period = 60
statistic = "Sum"
threshold = 1
treat_missing_data = "notBreaching"
dimensions = {
FunctionName = aws_lambda_function.capture.function_name
}
tags = merge(local.tags, {
Name = "${local.namespace}-${local.stage}-${local.name}-lambda-capture-errors-high"
"acme:severity" = "p1"
"acme:alarm-signal" = "errors-high"
"acme:monitored-service" = local.name
"acme:metric-namespace" = "AWS/Lambda"
})
}Alarm naming rules:
do:
follow namespace-stage-service-attributes order
include organization namespace, stage, detailed service, resource type, and signal
make service specific enough, for example api-public / api-blue / payment-dlq
include direction in signal: high / low / missing / unhealthy
keep alarm_name stable because notification routing and runbooks may reference it
use tags for severity, owner, runbook, dashboard, metric namespace, and escalation metadata
avoid:
severity prefix by default; use severity tag or optional suffix instead
CloudWatch metric namespace literal in alarm name, such as AWS/Lambda
full ARN or long target group ARN suffix in alarm name
threshold value in name unless it is part of SLO language
random suffix
resource ID only, such as i-1234567890abcdef13. Checklist#
must:
use tags for filtering and automation
keep Name human-readable and short
keep tag keys consistent across all resources
avoid secrets, customer names, tickets, or incident IDs in names/tags
check service-specific name length and character limits
prefer:
namespace-stage-name-attributes
lowercase values for generated names
short generated names for target groups
account/region in globally unique names such as S3 buckets
avoid:
inventing a different pattern per service team
encoding all metadata into resource names
parsing environment or subnet type from Name when tags can express it