What is an ARN - reading the address of everything

Logeshwaran.C

An ARN (Amazon Resource Name) is the exact string AWS uses to point at one specific thing in your account — a bucket, a function, a user, an instance — no matter which Region or account it lives in, written as colon-separated pieces like arn:aws:s3:::my-bucket. Here's the part almost nobody tells you: an ARN does not always have six parts. Two of those slots are allowed to sit empty on purpose, and that blank space is the format working correctly, not a resource missing its ID. In our last post, we had seen about What is a region and an availability zone in AWS, in this post let's see about what is ARN.

⚡ Quick Answer

The shapearn:partition:service:region:account-id:resource (three variants exist, split by a slash or a colon before the last part)

Find one you already have → look on the resource's details page in the console, or run aws sts get-caller-identity for your own identity

Build one yourself → needed for EC2 instances, which never show their ARN anywhere in the console

Full breakdown of every field, every shape, and every place people get it wrong is below.

Jake found this out the hard way on a Tuesday. He'd just set up a customer's backup script and needed to lock down an IAM policy so it could only touch one S3 bucket — nothing else in the account. He typed the bucket name into the policy's Resource field. It got rejected. Not the bucket name. The Resource field itself doesn't take a name. It takes an ARN.

What an ARN actually is, in plain English

Think about how a letter finds your house. It's not enough to write a name on the envelope — you need a street, a city, a postal code, a country. Strip any of those away and the letter either bounces or, worse, lands at the wrong house. An ARN is that same idea, except the "house" is a single AWS resource and the "country, city, street" are the account, the Region, and the service that owns it.

AWS's own Identity and Access Management (IAM) documentation puts it simply: Amazon Resource Names uniquely identify AWS resources, and you need one any time you have to point at a resource without any ambiguity — inside an IAM policy, inside a resource tag, inside an API call. If you've ever typed a bucket name into a policy and had it rejected, this is why: policies don't take names, they take addresses.

‍♂️ Jake's Reality Check

"Why do I need a whole address for it? Everything I own has one bucket named 'backups.' There's no confusion."

Because AWS doesn't know it's the only one. Somewhere out there, thousands of other accounts also have a bucket named "backups." Bucket names are unique across all of AWS globally, so that particular example is a bad one to worry about — but a Lambda function named "process-orders" absolutely is not unique. Your account has one. So does the customer's account next door. So does a demo account in Ireland nobody's touched in three years. The ARN is what tells AWS which "process-orders" you mean.

Ethan put it to Jake this way: a resource's plain name is like calling someone "Dave" in a room full of strangers. An ARN is Dave's full legal name, his employer, and his home address, all in one string, so there's exactly one person it could possibly mean.

Reading the address, field by field

Every ARN is built from colon-separated fields, and the general template — straight from AWS's own IAM documentation — looks like this:

arn:partition:service:region:account-id:resource

Six labeled slots. Here's what actually goes in each one, and what it means the first time you see the term.

Field What it means Typical value
arnA fixed word. Every single ARN starts with the literal text "arn" — it's not a variable, it never changes.arn
partitionWhich group of AWS Regions your account lives in. Nearly every account you'll ever touch uses one value here.aws (standard Regions), aws-cn (China), aws-us-gov (GovCloud)
serviceThe short internal name AWS gives the product — not the marketing name.s3, ec2, iam, lambda, dynamodb
regionThe AWS Region code the resource sits in. Left blank for services that aren't tied to one Region.us-east-2, eu-west-1
account-idThe 12-digit ID of the AWS account that owns the resource, no hyphens. Left blank for account-agnostic resources like public S3 buckets.123456789012
resourceThe specific thing itself — its type, its ID, or both, depending on the service.user/john, my-bucket-name

That last field, "resource," is where almost all the variation between services lives, and it's also where the counterintuitive part from the top of this post comes back in: Region and account ID are frequently left empty. IAM itself is the clearest example — an IAM user's ARN is arn:aws:iam::123456789012:user/john. Look closely at that string. There are two colons sitting right next to each other, right where the Region should be. That's not a typo. IAM is a global service; it doesn't live in one Region, so there's nothing to put there, and AWS's format simply leaves it empty rather than inventing a placeholder.

S3 goes a step further. A bucket ARN, arn:aws:s3:::my-bucket, skips both the Region and the account ID, leaving four colons in a row before the bucket name. Buckets are globally unique by name across every AWS account on the planet, so the account ID would be redundant information — the bucket name alone already tells you everything AWS needs to know which one you mean.

✅ Why this is worth memorizing

The moment you see a double colon in an ARN and know it's normal instead of broken, you'll stop wasting ten minutes staring at your own policy convinced you fat-fingered something. That single habit saves more debugging time than any other fact in this post.

The three shapes an ARN can take

AWS's IAM documentation lists exactly three general formats for that final "resource" field, and every service you'll ever touch uses one of them:

Shape Pattern Real example
ID only…:account-id:resource-idarn:aws:sns:us-east-1:123456789012:example-sns-topic-name
Slash-separated…:account-id:resource-type/resource-idarn:aws:iam::123456789012:user/john
Colon-separated…:account-id:resource-type:resource-idarn:aws:dms:us-east-1:123456789012:rep:QLXQZ64MH7CXF4QCQMGRVYVXAI

Notice that the choice of slash versus colon is entirely up to the service that owns the resource — it's not a rule you can predict from first principles, and AWS's own documentation is upfront that formats "are not perfectly consistent" across the platform. IAM, Lambda, and EC2 use the slash. Amazon RDS, AWS Database Migration Service, and Amazon DocumentDB use a colon. There's no shortcut here except checking the specific service's page — more on exactly where to look in a moment.

Some services push this further still. A resource identifier can include a parent resource nested inside it, written as sub-resource-type/parent-resource/sub-resource, or a version qualifier tacked on at the end as resource-type:resource-name:qualifier. You'll meet both of these later in this post, because Lambda uses the qualifier pattern for function versions, and it trips up almost everyone the first time they see it.

What an ARN looks like for the services you'll actually use

Rather than making you piece the format together yourself, here's a working reference table pulled from each service's own documentation. Bookmark this section — you'll come back to it.

Service Format
S3 bucketarn:aws:s3:::bucket-name
S3 objectarn:aws:s3:::bucket-name/key-name
IAM userarn:aws:iam::account-id:user/user-name
IAM rolearn:aws:iam::account-id:role/role-name
EC2 instancearn:aws:ec2:region:account-id:instance/instance-id
Lambda functionarn:aws:lambda:region:account-id:function:function-name
Lambda function (specific version)arn:aws:lambda:region:account-id:function:function-name:version
SNS topicarn:aws:sns:region:account-id:topic-name
RDS instancearn:aws:rds:region:account-id:db:db-instance-id
KMS keyarn:aws:kms:region:account-id:key/key-id
DMS replication instancearn:aws:dms:region:account-id:rep:resource-name

Two things worth calling out before you move on. First, the Lambda version qualifier: arn:aws:lambda:us-east-1:123456789012:function:my-function:3 points at version 3 of that function specifically, while arn:aws:lambda:us-east-1:123456789012:function:my-function with no trailing number points at whatever the "latest" alias currently resolves to. Mix these up in a deployment pipeline and you'll ship the wrong version without a single error message telling you why.

Second, S3 object paths. If your object lives inside folders — really just a naming convention S3 fakes using forward slashes — the whole path goes after the bucket name: arn:aws:s3:::my-bucket/photos/2026/vacation.jpg. IAM user and group names can carry the same kind of path, using only alphanumeric characters plus a specific allowed set: forward slash, plus sign, equals sign, comma, period, at sign, underscore, and hyphen. Anything outside that list in a path segment will get rejected.

Are ARNs secret? (The popular advice here is wrong)

You'll see plenty of forum answers treating an ARN like a password — never paste it anywhere, never put it in a support ticket, blur it out of screenshots. That instinct is understandable but it's overcorrecting. AWS's own IAM documentation states this directly: while an ARN, like any identifying information, should be shared carefully, it is not considered secret, sensitive, or confidential information on its own. An ARN alone can't authenticate you, can't move money, and can't grant access to anything by itself. It's an address, not a key.

That doesn't mean paste it everywhere without a second thought — an ARN reveals your account ID, which some teams treat as internal information, and it can tell an attacker exactly which resources exist and what they're named, which is useful reconnaissance for someone building a targeted attack. Redacting it from a public screenshot is reasonable caution, not paranoia. But if a support engineer, a teammate, or a Terraform error log needs the ARN to help you, sharing it will not, by itself, hand anyone access to anything.

⚠️ What actually IS sensitive

Your access keys, your secret access key, your session token, and your account's root password are the things that unlock a resource. An ARN just names it. Confusing the two — treating a shared ARN as a security incident while leaving an access key sitting in a public GitHub repo — is a common and dangerous mix-up.

Finding the ARN of a resource you already have

Most of the time you don't need to build an ARN from scratch — the resource already has one, and AWS just needs to hand it to you. There are three ways to get it, in order of how easy each one is.

  1. Check the console first. Open the relevant service's console, click into the resource, and look at its details or configuration page for a string starting with arn:aws:. Amazon S3, Amazon RDS, and AWS KMS all display the ARN directly on the resource's page and let you copy it with one click.
  2. Ask the CLI, if the console won't show it. Use the matching describe or get command for that resource type and look for a field ending in "Arn" in the response — DBInstanceArn for RDS, CertificateArn for ACM, and so on. For IAM resources specifically, a command like aws iam get-role --role-name YourRoleName returns the role's ARN directly in its output.
  3. Ask who you are, if it's your own identity you need. Run aws sts get-caller-identity with no arguments. It returns three fields — your user ID, your 12-digit account number, and the ARN of whichever identity is currently authenticated, whether that's an IAM user, an IAM role, or an assumed role session. No permissions are required to run this command, even if a policy explicitly denies you access to it, because the response contains nothing more sensitive than what an access-denied error would already show you.

That third option deserves its own moment, because it solves a genuinely common problem: "which account am I even in right now?" Anyone who's ever run a destructive command against the wrong AWS profile knows the sinking feeling. aws sts get-caller-identity is the fastest possible sanity check, and it works identically whether you're using a named CLI profile, an assumed role, or federated SSO credentials.

Identity type ARN shape you'll get back
IAM userarn:aws:iam::123456789012:user/username
Assumed IAM rolearn:aws:sts::123456789012:assumed-role/role-name/session-name
Root account userarn:aws:iam::123456789012:root

Ethan flagged something for Jake here that catches a lot of people off guard: an assumed role's ARN uses the sts service, not iam, and it carries a session name tacked onto the end that changes every time someone assumes that role fresh. Two logins five minutes apart from the same person, using the same role, will produce two different assumed-role ARNs — same role, different session. If you've written automation that tries to string-match a session ARN exactly, that's almost always the wrong thing to match against; match the role portion instead.

The one service that hides its own ARN: EC2

This is the counterintuitive detail that catches people who've been using AWS for years, not just beginners. Look at an EC2 instance in the console and you'll see its instance ID, its type, its IP address, its tags — but nowhere on that page will you find an ARN. AWS's Managed Services documentation confirms this is expected behavior, not a bug in your account: EC2 simply doesn't surface an ARN for an instance anywhere in the console interface, even though EC2 instances absolutely have ARNs and IAM policies require you to reference them by ARN if you want to lock a policy down to a specific instance.

So when you need one — say, writing an IAM policy that only lets a role start or stop one particular instance — you have to build it yourself. It's mechanical once you know the format.

  1. Copy the instance ID from the console or from aws ec2 describe-instances. It looks like i-0123456789abcdef0.
  2. Note the Region the instance is running in. This is whatever Region you had selected in the console top-right corner when you launched it, or the --region flag you used with the CLI — for example us-east-2.
  3. Get your 12-digit account ID by running aws sts get-caller-identity --query "Account" --output text, which returns just the number with no quotes around it.
  4. Assemble the three pieces into the EC2 instance format: arn:aws:ec2:region:account-id:instance/instance-id. For instance ID i-0123456789abcdef0, Region us-east-2, and account 123456789012, the finished ARN is arn:aws:ec2:us-east-2:123456789012:instance/i-0123456789abcdef0.

‍♂️ Jake's Reality Check

"Wait — so half the internet is telling me to write policies referencing something the console won't even show me?"

Yes, exactly that. It's a genuine rough edge in the platform, not something you're missing. Once you've built one EC2 instance ARN by hand you'll never need to look the format up again — it's always Region, account, then instance/ plus the instance ID.

EC2 has other resource types under the same service prefix that follow the identical pattern with a different word after the slash: image/ami-id for AMIs, volume/vol-id for EBS volumes, security-group/sg-id for security groups, snapshot/snap-id for EBS snapshots. Learn the instance format and the rest of EC2 falls into place the same way.

Wildcards: matching more than one resource at once

Writing out one ARN for every single resource a policy needs to cover gets unmanageable fast, so AWS lets you drop wildcard characters into certain parts of an ARN. There are two of them, and they behave differently.

The asterisk (*) — any number of characters

An asterisk stands in for zero or more of anything. arn:aws:iam::123456789012:role/* matches every single role in that account. arn:aws:iam::123456789012:role/service-* matches only roles whose name starts with "service" — things like service-role1 or service-test would both match, but my-service-role would not, because the asterisk only covers what comes after the fixed text, not before it.

You're not limited to using it inside the resource name, either. Asterisks can sit in any colon-separated section of the ARN. arn:aws:ec2:*:123456789012:vpc/* matches every VPC in every Region for that one account — useful when you genuinely don't care which Region a resource is in, only that it belongs to you.

The question mark (?) — exactly one character

A question mark matches precisely one character, no more, no fewer. AWS's own example is an S3 bucket full of folders that all follow a "four random characters, then dash, then the word test" naming scheme: arn:aws:s3:::amzn-s3-demo-bucket/????-test would match 1234-test, 2024-test, and a100-test, because each question mark consumes exactly one character regardless of what it is.

⚠️ Where wildcards are NOT allowed

Three places will reject a wildcard outright, and each rejection produces a different kind of failure:

The partition segment. You cannot wildcard aws itself — every ARN has to commit to one partition.

The resource-type segment. A policy like arn:aws:lambda:us-east-2:123456789012:functi*:my-function is explicitly called out as invalid in AWS's documentation — you can wildcard the resource name after the type, never the type word itself.

Principal and NotPrincipal policy elements. Wildcards work fine inside a Resource or NotResource element, but IAM's own reference is explicit that they are not permitted for Principal or NotPrincipal elements — different rule entirely from the ARN format itself.

The condition operator trap: ArnLike is not the same as StringLike

Here's a fix-that-comes-after-the-fix problem: you've built a correct ARN, put it inside a Condition block instead of the Resource element, and the policy still doesn't behave the way you expected. The usual cause is picking the wrong condition operator, because two of them look interchangeable and are not.

AWS's IAM policy elements documentation is explicit that ArnEquals and ArnLike behave identically to each other — both understand that they're comparing ARNs, not arbitrary strings. When you use ArnLike, AWS checks each of the six colon-delimited fields separately. Wildcards are allowed, but the partition, service, account-id, and resource-type fields still have to match exactly; only the Region and the resource-id portion are genuinely free to be partial matches.

StringLike looks like it should do the same job, since it also supports wildcards. It doesn't. StringLike ignores the ARN's internal structure completely and just does a flat character-by-character pattern match across the whole string, regardless of which colon-separated field the wildcard sits in.

‍♂️ Jake's Reality Check

"So if they both support wildcards, what's actually different in practice?"

Everything, once an account ID sneaks in somewhere you didn't expect. AWS's own documentation walks through this exact scenario: a condition using ArnLike to match against something like arn:aws:cloudtrail:*:111122223333:trail/* will correctly refuse to match a trail belonging to a completely different account, even if that different account's ARN happens to contain the string "111122223333" somewhere inside a resource name. StringLike, doing a dumb text match, would let that one through. It's a security-relevant difference, not a stylistic one.

Scenario ArnLike result StringLike result
Comparing two genuine ARNs where only Region or resource-id differMatches correctly, field-awareMatches correctly, but by text overlap, not structure
An account ID or resource ID your policy didn't intend appears as a substring elsewhere in the ARNRefuses to match — structure protects youCan match — a plain substring search doesn't know field boundaries

✅ Why this is the safer default

Use ArnLike or ArnEquals any time the value you're comparing against is genuinely an ARN. Reach for StringLike only when you're matching something that isn't shaped like an ARN at all — a plain resource name, a tag value, a free-text field. Treating an ARN like a generic string is the kind of shortcut that looks fine in testing and quietly widens access in production.

What happens if you leave part of the ARN out

Here's a detail that catches even experienced admins off guard. If you write an ARN with fewer than the standard six colon-separated fields inside an identity-based policy — the kind attached to a user, group, or role — AWS quietly fills in the missing fields with asterisks for you. Type just arn:aws:sqs and AWS treats it as identical to arn:aws:sqs:*:*:*, which grants access to every SQS queue, in every Region, across every account. That's an enormous, silent scope expansion from what looks like a small typo.

That auto-completion behavior does not apply everywhere, though, and this is the trap. Session policies — the ones you pass directly into AssumeRole, AssumeRoleWithWebIdentity, or AssumeRoleWithSAML calls — do not get this same courtesy. Passing an incomplete ARN into a session policy instead throws a MalformedPolicyDocumentException and the call fails outright. Same shortened ARN, two completely different outcomes depending on which kind of policy it's sitting inside.

✅ Why this is the safer habit either way

Write out every field explicitly, even the ones you'd leave blank on purpose like Region for IAM. A policy that says arn:aws:iam::123456789012:role/* tells the next person reading it exactly what's intentional. A policy that says arn:aws:iam and relies on silent auto-completion tells nobody anything, and it's one accidental copy-paste away from granting access to every account and Region you have.

Fixing "Invalid ARN" and its relatives

This is the error message that sends most people searching in the first place: you copy an ARN template straight out of AWS documentation, swap in your own account ID, and IAM's policy validator throws it back at you anyway. AWS's IAM Access Analyzer policy validation reference lists a whole family of related checks — Invalid ARN prefix, Invalid ARN Region, Invalid ARN account, Invalid ARN service case, Invalid ARN resource, ARN Region not allowed, ARN account not allowed, and Missing ARN field are all separate, specific findings, not one generic error.

Work through the checklist in this order — cheapest checks first:

  1. Count the colons. A correct ARN has exactly the right number of colon-separated fields for that service's format — no more, no fewer. A common mistake is dropping one of the two blank colons that IAM or S3 ARNs legitimately require, collapsing iam:: down to a single colon by accident.
  2. Check the service name for typos and case. Service segments are lowercase and exact — sts is not interchangeable with iam even though both can appear in similar-looking ARNs for related concepts like federated users, and a name typed with the wrong capitalization triggers an "Invalid ARN service case" finding specifically.
  3. Confirm the resource-type word matches what that service expects. "Invalid ARN resource" fires when the segment after the account ID doesn't match a recognized resource type for that service, or doesn't match the separator (slash versus colon) that service uses.
  4. Verify the account ID is exactly 12 digits with no hyphens and that it's actually the account you think it is — "Invalid ARN account" and "ARN account not allowed" are two different findings depending on whether the number is malformed or simply not permitted for that policy's scope.
  5. Make sure you haven't left a required field empty where the service expects a real value — "Missing ARN field" and "Missing ARN Region" cover exactly this, and they're common when someone copies an S3-style empty-Region ARN and pastes it into a policy for a Regional service like EC2.

Real example of step 2 tripping someone up: a developer copying an AWS STS federated-user ARN template, arn:aws:sts::123456789012:federated-user/name, accidentally typed the service as aim instead of iam in a different but related field, and the policy silently accepted characters that didn't match any real service at all — the fix was reading the ARN character by character against the documented example rather than trusting a quick visual scan.

Reading someone else's ARN (cross-account access and security review)

One of an ARN's most practical uses is that it lets a policy in your account point at a resource that lives in a completely different AWS account. Because the account ID is baked into the ARN itself, an IAM policy for a bucket sitting in Account A can be attached to a role in Account B, and the two will agree on exactly which bucket is meant, with no ambiguity, no matter what the bucket happens to be named.

If you're handed an ARN and asked to figure out what it points to — a common task during a security review — there's no single dashboard where you paste an ARN and get back full resource details for something in another account, because AWS doesn't expose cross-account resource lookups that way. What you can reliably pull straight out of the string itself:

  • The service segment tells you which console to even open.
  • The account ID tells you whether the resource belongs to your own organization or an outside party.
  • The resource type and ID confirm you've found the exact right thing once you're looking at candidates in that service's console or CLI output.

‍♂️ Jake's Reality Check

"A vendor sent me an ARN and asked me to add it to a resource policy. Can I just trust it's real without seeing the actual resource?"

You should always confirm what you're granting access to before you grant it — an ARN alone can't prove a resource exists or that the vendor legitimately controls it. Ask the vendor to run aws sts get-caller-identity themselves and confirm the account number matches what's in the ARN they gave you, and confirm through a second channel (their own documentation, a signed agreement, a phone call) rather than taking a pasted string at face value.

Here's what you genuinely cannot do, and it's worth saying plainly rather than pretending otherwise: if you don't have access to the account that owns a resource, an ARN alone will not let you retrieve that resource's contents, tags, configuration, or any detail beyond what's already encoded in the string itself. An ARN identifies; it doesn't authorize. Not having account access means the door stays shut no matter how correctly formatted the address on it is.

ARNs as a security control: the confused deputy problem

There's a specific, well-documented AWS security pattern that only exists because ARNs can identify a resource down to the exact account and ID: the confused deputy problem. It happens when one AWS service calls into another service on your behalf, and a role you granted to that first service gets tricked into acting on a resource that belongs to someone else entirely — not because your permissions were wrong, but because the calling service didn't check whose resource it was actually touching.

AWS's own guidance across many service documentation pages — Amazon RDS, AWS Storage Gateway, EventBridge Scheduler, and AWS PCS among them — recommends the same fix every time: add an aws:SourceArn condition to the trust policy or resource policy involved, scoped to the exact ARN of the resource that's allowed to trigger the action. If you genuinely don't know the full ARN in advance, or need to cover several resources, you can wildcard the unknown portions rather than leaving the condition off entirely — something like arn:aws:servicename:*:123456789012:* still locks the call to your account even without pinning down every field.

There's a wrinkle worth knowing before you copy this pattern: some ARNs, most notably S3 bucket ARNs, don't carry an account ID at all — you already saw why earlier in this post. AWS's documentation flags this directly: when the aws:SourceArn value doesn't include an account ID, you need to pair it with the separate aws:SourceAccount condition key as well, or the missing account field leaves a gap the ARN alone can't close.

Ethan's summary for Jake here was blunt: this is exactly why the "ARNs aren't secret, but be reasonably careful with them" line from earlier in this post matters in practice. The confused deputy pattern only works as a defense because the ARN is specific enough to identify one exact resource. A vague policy that trusts "any resource this service might call" is the mistake this whole condition key exists to prevent.

Where ARNs actually show up in daily work

Once you know to look for them, ARNs turn up constantly, in places that have nothing to do with each other on the surface:

IAM policies. Every Resource and NotResource element in a permissions policy takes an ARN, not a friendly name. This is the single most common place a beginner meets the format for the first time, usually while trying to lock a policy down to one specific resource instead of granting broad access.

Resource tagging. Services like Amazon RDS require the ARN specifically — not the instance identifier — when you're adding or removing tags through the API, because tagging operations need to unambiguously identify one resource across the whole account.

Cross-service triggers. When API Gateway invokes a Lambda function, or an S3 upload triggers a function, or an EventBridge rule fires off a Step Functions execution, the target is always specified by its ARN. This is the plumbing that lets services that were never designed together still call into each other reliably.

CLI and SDK calls that accept either a name or an ARN. Some AWS CLI commands are flexible about this — Lambda's get-function-configuration, for instance, will accept either the plain function name or its full ARN in the --function-name argument, which is handy when you already have the ARN sitting in a variable from a previous command and don't want to parse the name back out of it.

CloudFormation: why Ref sometimes returns an ARN and sometimes doesn't

Infrastructure-as-code is one of the most common places people go looking for a resource's ARN before it even exists yet — you're writing a template, and a later resource in that same template needs to reference one being created earlier. CloudFormation gives you two different intrinsic functions for this, and they don't always return the same kind of value, which is where people get tripped up.

Ref returns whatever CloudFormation considers that resource type's primary identifier — and that's inconsistent across services in a way that mirrors the ARN inconsistency covered earlier in this post. For an AWS::SNS::Topic, Ref actually does return the topic's ARN directly. For an AWS::S3::Bucket, Ref returns just the bucket name, not the ARN — you have to reach for Fn::GetAtt with the Arn attribute to get the full address. For an AWS::EC2::Instance, Ref returns the instance ID, again not the ARN.

Fn::GetAtt is the more reliable choice whenever you specifically need an ARN, because most resource types that support an ARN attribute expose it consistently as .Arn!GetAtt MyBucket.Arn, !GetAtt MyFunction.Arn, and so on. Check that resource type's own CloudFormation reference page for the exact attribute name before assuming, since a handful of resource types use a different attribute name for historical reasons.

Resource type Ref returns Get the ARN with
AWS::S3::BucketBucket nameFn::GetAtt with the Arn attribute
AWS::SNS::TopicTopic ARN directlyRef already gives you the ARN
AWS::EC2::InstanceInstance IDFn::GetAtt with the Arn attribute, or build it manually as covered earlier
AWS::Lambda::FunctionFunction nameFn::GetAtt with the Arn attribute
AWS::IAM::RoleRole nameFn::GetAtt with the Arn attribute

The practical failure mode: someone plugs a Ref straight into a cross-stack IAM policy expecting an ARN, the stack deploys without error because a plain resource name is still valid input to plenty of fields, and the policy silently fails to grant the access it was supposed to grant — because IAM's Resource element wanted an ARN and got a bare name instead. CloudFormation won't catch this for you at deploy time in every case; the fix is checking each resource type's actual Ref return value against its documentation rather than assuming it always hands back the ARN.

Does an ARN ever change?

For most resources, no — once assigned, an ARN stays fixed for the lifetime of that resource, which is exactly the point of using it as an identifier in the first place. But there are two situations worth knowing about, because they genuinely confuse people who assumed ARNs never budge.

The first is deletion and recreation. If you delete an S3 bucket named reports-bucket and someone else later claims that exact name for their own bucket in a different account, the ARN arn:aws:s3:::reports-bucket now points at a completely different resource owned by a completely different party. The string itself hasn't "changed," but what it refers to has — this is exactly why bucket names are globally unique and why released names can be reused by anyone.

The second is renaming. Some resources can be renamed after creation, and depending on the service, the ARN either updates to reflect the new name or stays tied to an internal ID that never surfaces to you at all. This varies enough by service that the safe assumption is: treat a stored ARN as potentially stale after a rename event, and re-fetch it rather than trusting a cached copy indefinitely, particularly in automation that runs infrequently.

Beyond the basics: paths, qualifiers, and the deeper structure

Two structural patterns show up often enough to name explicitly, and neither is covered by the basic three-shape rule from earlier.

Nested sub-resources. AWS's own IAM reference describes this shape as sub-resource-type/parent-resource/sub-resource. An Elastic Beanstalk application version is a real example: its ARN format is arn:aws:elasticbeanstalk:region:account-id:applicationversion/application-name/version-label, where the resource itself is nested two levels deep inside an application, which is itself nested inside a Region and account.

Qualifiers. Written as resource-type:resource-name:qualifier, this pattern lets one base resource have multiple addressable variants. Lambda's version numbers, discussed earlier, are the textbook example — the qualifier segment distinguishes my-function:1 from my-function:2 from my-function:$LATEST, all under the same underlying function.

✅ Ethan's take

"Honestly, the six-field template is the least useful part of learning ARNs, even though it's what every article leads with. The part that actually saves you time is knowing that every service documents its own exact format under a 'resource types' table somewhere, and that looking it up takes thirty seconds once you know the page exists. Memorizing the general shape gets you 80% of the way there and then you look up the last 20% every single time, forever, and that's completely fine."

Looking up the exact format for a service you've never used

Given how much variation exists between services, the actual skill isn't memorizing every format — it's knowing where to check when you hit an unfamiliar one. AWS's IAM documentation points to exactly one canonical place for this: the Service Authorization Reference. Open that page, find the specific AWS service you're working with, and scroll to its resource types table. Every resource type that service defines is listed there along with its exact ARN template, condition keys it supports, and any relevant notes about that resource's quirks.

This beats guessing from a similar-looking service every time, because — as covered above — formats genuinely aren't consistent across AWS. A format that works for RDS will not work for DynamoDB just because both are databases.

You need to... Go here
Confirm the exact ARN format for any serviceService Authorization Reference → that service's page → resource types table
Understand why a policy validation check is failingIAM policy validation check reference
See the general ARN rules that apply everywhereIAM User Guide, "Identify AWS resources with Amazon Resource Names (ARNs)"

Frequently asked questions

What does ARN stand for in AWS?

Amazon Resource Name. It's the standard string format AWS uses across every service to unambiguously identify a single resource — combining the partition, service, Region, account, and resource identifier into one address.

Is an ARN the same as a resource ID?

No, and this is the mix-up that trips up the most beginners. A resource ID — like an EC2 instance ID (i-0123456789abcdef0) — is just one piece embedded inside the ARN. The full ARN also carries the service, Region, and account ID around that piece, which is exactly what makes it unambiguous across your whole AWS footprint while the bare ID alone would not be.

Are ARNs case-sensitive?

The fixed parts are effectively case-sensitive in practice — a mismatched case on the service segment triggers its own specific validation finding ("Invalid ARN service case") separate from a plain typo. Resource names within the ARN follow whatever case rules that resource type itself allows; some resource names are case-sensitive, others aren't, so match the case of the real resource exactly rather than assuming.

Can two resources ever have the same ARN?

No — that's the entire point of the format. Because the account ID, Region, service, and resource identifier are all folded into one string, two genuinely different resources cannot produce an identical ARN unless one has been deleted and its name reused by a new resource in the same slot, which is a different situation covered above under permanence.

Why doesn't my EC2 instance ARN show up in the console?

This is documented, expected behavior, not a bug specific to your account — EC2 simply doesn't display instance ARNs in its console interface anywhere. You build the ARN yourself using the instance ID, Region, and account ID, following the steps covered earlier in this post.

What's the difference between arn:aws, arn:aws-cn, and arn:aws-us-gov?

These are the three supported partitions. aws covers standard commercial AWS Regions, which is what almost every account uses. aws-cn is for the separate China Regions, and aws-us-gov is for AWS GovCloud (US). A resource in one partition cannot be referenced from a policy written for a different partition — they're effectively separate universes.

Can an ARN change over time?

For a given living resource, no — it stays fixed. But if the resource is deleted and its name gets reused by someone else, or the service supports renaming with an ARN tied to the display name rather than an internal ID, the ARN string as written can end up pointing somewhere different than it once did. Re-fetch stored ARNs periodically in long-running automation rather than assuming they're permanent forever.

Is it safe to share an ARN publicly?

AWS's own documentation states ARNs are not considered secret, sensitive, or confidential information, unlike access keys or passwords. Reasonable caution still applies — an ARN reveals your account ID and resource names, which is useful reconnaissance information, so redacting it from public screenshots is sensible even though sharing it with support or teammates carries no direct security risk on its own.

Why do I get "Invalid ARN Resource" when I copy an ARN straight from the docs?

Usually because you've swapped in your own account ID or resource name but left a formatting detail from the original example intact by mistake — a mismatched separator (slash where a colon belongs), a wrong resource-type word for that service, or a missing field. Walk through the checklist in this post's fixing section field by field rather than guessing.

Can I use a wildcard for the account ID in an ARN?

Yes, an asterisk can sit in the account-id position just like any other section, matching all accounts for that pattern where the policy context allows it. What you cannot do is wildcard the partition segment or the resource-type word itself — those two positions specifically reject wildcards.

What's the ARN for the AWS account itself (the root user)?

It follows the pattern arn:aws:iam::account-id:root — no user name, just the literal word "root" after the account ID. This is what you'll see if you run get-caller-identity while authenticated as the account's root user rather than an IAM user or role.

How do I get the ARN of the identity I'm currently using?

Run aws sts get-caller-identity with no arguments. It returns your account ID, your user ID, and the ARN of your currently authenticated identity in one response, and it requires no special permissions to run.

Why does my Lambda function have more than one ARN?

A function's unqualified ARN (no trailing number) always resolves to the alias you're currently pointing at, typically the latest published version, while each specific published version gets its own qualified ARN with a version number appended after a final colon. Both ARNs are valid and both point at the same underlying function — they just differ in whether they're pinned to one exact version or float with whatever is currently deployed.

What happens if I paste an incomplete ARN into an identity-based policy?

AWS automatically fills every missing field with a wildcard asterisk, silently widening the scope of what the policy grants — sometimes far beyond what was intended. The same shortcut does not work in session policies passed to AssumeRole and similar STS calls; there, an incomplete ARN throws a MalformedPolicyDocumentException and the request fails outright instead of being auto-completed.

Do S3 object ARNs include the region?

No. S3 bucket and object ARNs omit both the Region and the account ID entirely, leaving four colons in a row before the bucket name: arn:aws:s3:::bucket-name/object-key. This is because bucket names are globally unique across all of AWS, making the Region and account fields redundant for identifying which object you mean.

How can I find the correct ARN format for a service I've never used before?

Open the AWS Service Authorization Reference, find that specific service's page, and scroll to its resource types table. Every resource type it supports is listed there with its exact template, which is far more reliable than guessing based on how a similar-sounding service formats its own ARNs.

Revision note. Written September 2026, covering the ARN format. This will need a revisit if AWS adds a fourth partition or changes how incomplete ARNs are auto-completed in identity-based policies. If you've been stuck on an "Invalid ARN" error for the last hour, take a breath — it's almost always one missing colon, not something wrong with you.

πŸ’‘ Recommended AWS Foundations Reading

Master the core building blocks of AWS infrastructure, networking, and security:

Related