Demystify AWS IAM with this comprehensive guide covering ARN format, IAM roles, S3 bucket ARNs, policy syntax, trust relationships, and real-world access control examples.
AWS Identity and Access Management (IAM) is the foundation of every secure AWS deployment. Whether you're granting an EC2 instance access to S3, restricting who can invoke a Lambda function, or setting up cross-account access, IAM is the mechanism. And Amazon Resource Names (ARNs) are the universal identifiers that make IAM policies precise and unambiguous.
This guide demystifies IAM — ARN format, roles vs users vs groups, S3 bucket ARNs, policy syntax, and the least-privilege patterns that keep AWS accounts secure.
What Is an ARN?
An Amazon Resource Name (ARN) is a globally unique identifier for any AWS resource. Every IAM policy, every S3 bucket, every EC2 instance, Lambda function, and DynamoDB table has one.
The ARN format:
arn:partition:service:region:account-id:resource-type/resource-id
# Examples:
arn:aws:iam::123456789012:user/alice
arn:aws:iam::123456789012:role/LambdaS3ReadRole
arn:aws:s3:::my-app-bucket
arn:aws:s3:::my-app-bucket/*
arn:aws:lambda:us-east-1:123456789012:function:ProcessOrders
arn:aws:dynamodb:us-east-1:123456789012:table/UsersBreaking down each segment:
Segment | Value | Notes |
|---|---|---|
partition | aws | aws-cn for China, aws-us-gov for GovCloud |
service | iam, s3, ec2, lambda… | AWS service namespace |
region | us-east-1 | Empty for global services (IAM, S3) |
account-id | 123456789012 | 12-digit AWS account ID |
resource | user/alice | Type + name (format varies by service) |
IAM Identity Types
IAM Users
A permanent identity with long-term credentials (access key + secret key). Use IAM users for human operators accessing AWS from the CLI or Console. Avoid sharing users — one per person.
IAM Groups
A collection of IAM users. Attach policies to groups rather than individual users — managing permissions for a team of 20 developers through a group is far easier than managing 20 separate user policies.
IAM Roles
Temporary credentials that AWS services and applications assume. Roles have no long-term credentials. Best practice: Always use roles (not IAM users) for EC2 instances, Lambda functions, ECS tasks, and any AWS service that needs to access other AWS resources.
# Role ARN format
arn:aws:iam::123456789012:role/RoleName
# IAM User ARN format
arn:aws:iam::123456789012:user/username
# IAM Group ARN format
arn:aws:iam::123456789012:group/groupnameS3 Bucket ARNs
S3 ARNs are unusual because S3 bucket names are globally unique — there's no region or account ID in the ARN:
# The bucket itself
arn:aws:s3:::my-app-uploads
# All objects in the bucket
arn:aws:s3:::my-app-uploads/*
# A specific object
arn:aws:s3:::my-app-uploads/user-avatars/profile.jpg
# All objects with a prefix
arn:aws:s3:::my-app-uploads/user-avatars/*Critical pattern: When writing S3 bucket policies, you almost always need both the bucket ARN and the /* ARN together — one for bucket-level operations (like listing) and one for object-level operations (like GetObject and PutObject).
IAM Policy Structure
{
"Version": "2012-10-17", // Always this value
"Statement": [
{
"Sid": "AllowS3ReadAccess", // Optional statement ID
"Effect": "Allow", // Allow or Deny
"Action": [
"s3:GetObject",
"s3:ListBucket"
],
"Resource": [
"arn:aws:s3:::my-app-uploads", // For s3:ListBucket
"arn:aws:s3:::my-app-uploads/*" // For s3:GetObject
],
"Condition": { // Optional conditions
"StringEquals": {
"s3:prefix": ["user-avatars/"]
}
}
}
]
}Creating an IAM Role for Lambda to Access S3
This is one of the most common IAM patterns. Two documents are involved:
1. Trust Policy (who can assume the role)
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "lambda.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
}2. Permission Policy (what the role can do)
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "S3ReadWrite",
"Effect": "Allow",
"Action": [
"s3:GetObject",
"s3:PutObject",
"s3:DeleteObject"
],
"Resource": "arn:aws:s3:::my-lambda-bucket/*"
},
{
"Sid": "S3List",
"Effect": "Allow",
"Action": "s3:ListBucket",
"Resource": "arn:aws:s3:::my-lambda-bucket"
},
{
"Sid": "CloudWatchLogs",
"Effect": "Allow",
"Action": [
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents"
],
"Resource": "arn:aws:logs:*:*:*"
}
]
}S3 Bucket Policy vs IAM Policy
Feature | IAM Policy | S3 Bucket Policy |
|---|---|---|
Attached to | IAM user/role/group | S3 bucket |
Cross-account | Needs both IAM + bucket policy | Can grant directly |
Public access | Cannot make public | Can allow public read |
Principal field | Not needed (implicit) | Required |
Public read bucket policy (for static websites)
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "PublicReadGetObject",
"Effect": "Allow",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::my-static-website/*"
}
]
}IAM Wildcards in ARNs
# Match any resource in any account
arn:aws:s3:::*
# Match any object in a specific bucket
arn:aws:s3:::my-bucket/*
# Match any Lambda function in any region, this account
arn:aws:lambda:*:123456789012:function:*
# Match Lambda functions with a specific prefix
arn:aws:lambda:us-east-1:123456789012:function:prod-*AWS CLI: Working with ARNs
# Get your account ID (useful for building ARNs)
aws sts get-caller-identity --query Account --output text
# List IAM roles with their ARNs
aws iam list-roles --query 'Roles[*].[RoleName,Arn]' --output table
# Get a specific role's ARN
aws iam get-role --role-name MyLambdaRole --query 'Role.Arn' --output text
# Get S3 bucket ARN (they always follow this format)
echo "arn:aws:s3:::$(aws s3api list-buckets --query 'Buckets[0].Name' --output text)"Least-Privilege Best Practices
Never use AdministratorAccess for application roles — grant only the specific actions and resources the role needs
Use resource-level permissions — specify the exact bucket ARN and
/*ARN, not*Separate roles by function — read-only role for reporting, read-write role for the app
Enable MFA for IAM users — especially for console access and delete operations
Use IAM Access Analyzer — it identifies policies that grant more access than actually used
Rotate access keys — or better, replace IAM user keys with IAM roles for EC2/Lambda
Enable CloudTrail — every IAM API call is logged; essential for auditing and incident response
Conclusion
IAM and ARNs are foundational to AWS security. Once you understand ARN format, the difference between users, groups, and roles, and how trust policies and permission policies work together, you can construct precise access controls that follow the principle of least privilege. For S3, always pair the bucket ARN and /* ARN in policies. For compute services (Lambda, EC2, ECS), always use roles over long-term IAM user credentials. Start with IAM Access Analyzer to audit existing policies and tighten anything that's overly permissive.










