Back to Blog
DevOps

AWS IAM Roles, ARNs, and S3 Bucket Policies Explained (Complete Guide)

19 July 202610 min read
AWS IAM Roles, ARNs, and S3 Bucket Policies Explained (Complete Guide)

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/Users

Breaking 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/groupname

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

Was this article helpful?

C

Written by

CoderCrafter Team

Developer & Technical Writer

Building developer tools and writing practical engineering content at CoderCrafter. Covers JavaScript, React, Node.js, DevOps, and modern web development.

Comments

Leave a comment

0/2000

Comments are reviewed before appearing. Be kind and constructive.

Call UsWhatsApp