How to Access an S3 Bucket Across AWS Accounts Using IAM Roles

(No Access Keys)
This blog explains a production-grade, AWS-recommended solution using IAM Roles and STS AssumeRole.
No access keys.
No secrets.
No hardcoding.
Why This Matters
Many teams still:
Share access keys ❌
Store secrets in files or environment variables ❌
Use overly permissive bucket policies ❌
All of these approaches are risky and hard to audit.
AWS provides a secure, least-privilege mechanism for this exact problem:
Cross-account IAM roles with STS AssumeRole.
The Goal
EC2 runs in Account A
S3 bucket exists in Account B
EC2 can access the bucket securely
No access keys or secrets
IAM roles only
Automatic credential rotation
High-Level Architecture
EC2 (Account A)
└── IAM Role (EC2 Base Role)
└── sts:AssumeRole
└── IAM Role (S3 Access Role – Account B)
└── S3 Bucket
Important rule: An EC2 instance can only attach an IAM role from its own account.
Cross-account access always happens using STS AssumeRole.
Example Accounts (Dummy Values)
| Purpose | Account ID |
| EC2 account | 111111111111 |
| S3 account | 222222222222 |
Account B Setup (S3 Owner)
Step 1: Create an IAM Role in Account B
Role name:
cross-account-s3-role
Role ARN:
arn:aws:iam::222222222222:role/cross-account-s3-role
This role represents who is allowed to access the S3 bucket.
Step 2: Attach IAM Policy to the Role
This policy defines what the role can do.
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "s3:ListBucket",
"Resource": "arn:aws:s3:::example-content-bucket"
},
{
"Effect": "Allow",
"Action": [
"s3:GetObject",
"s3:PutObject"
],
"Resource": "arn:aws:s3:::example-content-bucket/*"
}
]
}
Step 3: Add Bucket Policy to the S3 Bucket
Bucket name:
example-content-bucket
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::222222222222:role/cross-account-s3-role"
},
"Action": "s3:ListBucket",
"Resource": "arn:aws:s3:::example-content-bucket"
},
{
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::222222222222:role/cross-account-s3-role"
},
"Action": [
"s3:GetObject",
"s3:PutObject"
],
"Resource": "arn:aws:s3:::example-content-bucket/*"
}
]
}
Step 4: Configure Trust Relationship (Critical)
This allows Account A to assume the role.
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::111111111111:root"
},
"Action": "sts:AssumeRole"
}
]
}
Account B setup is now complete.
Account A Setup (EC2 Owner)
Step 5: Create an IAM Role for EC2
Role name:
ec2-cross-account-role
Role ARN:
arn:aws:iam::111111111111:role/ec2-cross-account-role
This role will be attached to the EC2 instance.
Step 6: Allow This Role to Assume the S3 Role
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "sts:AssumeRole",
"Resource": "arn:aws:iam::222222222222:role/cross-account-s3-role"
}
]
}
Note: This role does not have direct S3 permissions.
Step 7: Trust Relationship for EC2 Role
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "ec2.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
}
Step 8: Create Instance Profile (Required)
aws iam create-instance-profile \
--instance-profile-name ec2-cross-account-role
aws iam add-role-to-instance-profile \
--instance-profile-name ec2-cross-account-role \
--role-name ec2-cross-account-role
Attach this role to the EC2 instance.
Validation on EC2
Step 9: Verify EC2 Role
aws sts get-caller-identity
Expected output:
{
"Account": "111111111111",
"Arn": "arn:aws:sts::111111111111:assumed-role/ec2-cross-account-role/..."
}
Permanent Access (No Keys, No Manual AssumeRole)
Step 10: Configure AWS CLI for Auto-Assume
mkdir -p ~/.aws
nano ~/.aws/config
[default]
region = ap-south-1
[profile s3-cross-account]
role_arn = arn:aws:iam::222222222222:role/cross-account-s3-role
credential_source = Ec2InstanceMetadata
region = ap-south-1
Important:
No ~/.aws/credentials file
No access keys
Uses EC2 metadata automatically
Step 11: Access S3 Bucket
aws s3 ls s3://example-content-bucket --profile s3-cross-account
This will work permanently.
Final Outcome
Secure cross-account S3 access
IAM roles only
No secrets stored anywhere
Automatic credential rotation
Fully auditable
Production-ready
Key Takeaways
EC2 cannot attach roles from another account
Cross-account access always uses STS AssumeRole
IAM roles are the credentials
AWS CLI supports permanent auto-assume
This is the AWS-recommended pattern
When to Use This Pattern
Multi-account AWS environments
Shared S3 buckets
Centralized data platforms
CI/CD pipelines
Bastion or admin EC2 instances



