Skip to content

ARNs and identifiers

Identifiers are the least reversible decision in the platform. They appear in policies, in metering records, in audit records, in error messages, in customer scripts and in infrastructure state files, from the first request onwards.

Read this page before implementing anything that mints an identifier.

  • None
arn:<partition>:<service>:<region>:<account-id>:<resource-type>/<resource-id>
arn:aws-shelf:ec2:hel1:123456789012:instance/i-0a1b2c3d4e5f60718
arn:aws-shelf:iam::123456789012:user/deploy
arn:aws-shelf:billing:hel1:123456789012:invoice/2026-000123

The region segment is empty for global services. The account segment is present except for resources owned by the platform rather than by a customer.

PROPOSED: aws-shelf.

The partition cannot be an arbitrary word. The Terraform AWS provider validates partitions against ^aws(-[a-z]+)*$, and it constructs ARNs itself — in aws_iam_policy_document, in the aws_arn data source, and in every resource that synthesises one. A partition outside that pattern is rejected by the provider, and the ARNs the provider generates would not match ours.

Additionally, and permanently, ARNs arriving with the aws partition are accepted on input. Terraform derives the partition from provider metadata and will generate arn:aws: for a region it does not recognise, so a policy written through it would otherwise never match anything.

This is the least reversible decision in the document set: it is embedded in every stored policy and every Terraform state file the moment a customer writes one.

The separator between resource type and resource id is a slash for every resource type currently defined.

Where a future service mirrors an AWS service that uses the colon form — resource-type:id — that service documents the colon form for its own resources. The grammar permits both; the platform does not silently change the form of an existing resource type.

PROPOSED: twelve decimal digits, never beginning with zero. See Accounts and tenancy.

PROPOSED: a type prefix, a hyphen, and 17 lowercase hexadecimal characters.

i-0a1b2c3d4e5f60718

Seventeen hexadecimal characters because that is the modern AWS long-id format, and because customer tooling validates against it. The Terraform AWS provider, among others, validates instance ids against ^i-([0-9a-f]{8}|[0-9a-f]{17})$. Seventeen hex is safe; anything else is not.

PrefixResource
i-Instance
ami-Image
vol-Volume
snap-Snapshot
key-Key pair
eni-Network interface
sg-Security group
vpc-Network
subnet-Subnet
quo-Quote
ord-Order

Prefixes for resources that do not exist in v1 are reserved here so that they cannot later be assigned to something else.

These ids are opaque. Customers must not parse them, and nothing in one encodes the account, the region, the creation time, or anything about the hardware.

PROPOSED: ids are generated from a cryptographically secure source, so that one id reveals nothing about the existence, count or ordering of others.

Invoice numbers are not opaque and not random:

2026-000123

They are sequential, gapless within a sequence, and human-readable, because tax authorities require gapless sequential numbering of invoices. An opaque random identifier would not satisfy that.

This is a deliberate exception to the opacity rule rather than an inconsistency, and it applies only to documents that are legal records: invoices and credit notes. Quotes and orders, which are not legal records, use opaque ids.

OPEN (Michael): the exact invoice numbering scheme, and whether the sequence is per-account or global.

Where a resource carries a name the customer chooses — a key pair name, a policy name — the constraints are documented on that resource and are never narrowed after release.

PROPOSED: where an AWS counterpart exists, its constraints are adopted exactly. Tooling validates names client-side before calling, so a narrower rule would reject names the client believes are valid, and a wider one would accept names the client refuses to send.

Service names, region names and resource ids are lowercase, and comparison is case-sensitive.

Two exceptions, both real:

  • Access key ids are uppercase and case-sensitive. A verifier that lowercases them before lookup breaks every signature.
  • HTTP header names are matched case-insensitively by HTTP, and are lowercased only when building the canonical request for a signature.