Naming


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.html

1. 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 id

This 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 sg

This 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 name

Add 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 enough

Practical 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 policy

Minimum tags:

Project
Environment
ManagedBy

Good default:

Project
Environment
Region
Stack
Service
ManagedBy

Production / organization tags:

Project
Environment
Region
Stack
Service
Owner
Team
CostCenter
Criticality
ManagedBy

Terraform 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-endpoint

6. 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_name

Why 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-1234567890abcdef

13. 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