A
Arun's Blog
All Posts

Cross-Account S3 Bucket Access via IAM Roles and Bucket Policies

|4 min read|
CLIS3SecurityIAM
TL;DR

Share S3 buckets across AWS accounts by creating an IAM role in the accessing account with S3 permissions, attaching that role to an EC2 instance, then adding a bucket policy in the source account that grants access to the cross-account role ARN.

Introduction

There are a few ways to share an S3 bucket across AWS accounts. The most common pattern is: create an IAM role in the accessing account, attach it to the resource that needs access (an EC2 instance in this example), then add a bucket policy in the source account that grants access to that role's ARN. Two-sided trust, easy to audit, no shared access keys to rotate.

This post walks through the full setup with AWS CLI commands. The example uses s3:* permissions to keep the demo short. Tighten to the specific actions you need (s3:GetObject, s3:PutObject, etc.) for production.

Prerequisites

  • Account A to be the owner of the S3 bucket you want to share
  • Account B to be the account that will hold a role to be used to access the S3 bucket in Account A
  • Adequate permissions in both AWS accounts to accomplish the tasks
  • AWS CLI installed
Security Warning

The example below grants s3:* permissions for demonstration purposes. In production, always follow the principle of least privilege - grant only the specific S3 actions needed (e.g., s3:GetObject, s3:PutObject) rather than wildcard permissions.

Account A

  1. Create a S3 bucket called 'mybucket2share'
    • aws s3 mb mybucket2share

Account B

In Account B I create an EC2 service role (call it ec2role2accessmybucket2share) with the standard EC2 trust policy that lets ec2.amazonaws.com assume it via sts:AssumeRole. Then I create a managed policy (mycrossaccounts3bucketpolicy) that allows the S3 actions you need on both arn:aws:s3:::mybucket2share and arn:aws:s3:::mybucket2share/*. Attach the policy to the role with aws iam attach-role-policy, then launch an EC2 instance with --iam-instance-profile Name=ec2role2accessmybucket2share so the instance picks up the role on boot. I usually grab the current Account B ID with aws sts get-caller-identity --query Account --output text and substitute it into the policy ARN at attach time.

Pro Tip

Use IAM roles instead of IAM users for cross-account access whenever possible. Roles provide temporary credentials that automatically rotate, eliminating the need to manage long-term access keys.

Back to Account A

Back on Account A, apply a bucket policy to mybucket2share via aws s3api put-bucket-policy. The policy has a single Allow statement with Principal.AWS pointing at the full role ARN from Account B (arn:aws:iam::<Account-B-ID>:role/ec2role2accessmybucket2share), the S3 actions you want to permit, and the bucket and bucket/* as the resource. This is the cross-account trust piece, and it must reference the role ARN exactly. A typo in the account ID gives a silent AccessDenied at runtime.

Note

Replace '123456' with the actual 12-digit AWS Account ID of Account B. The bucket policy grants access to the specific IAM role ARN, ensuring only that role can access the bucket.

Back to Account B

  1. Log into the EC2
  2. Create a file called 'mytestfile.txt' with the text content of 'test' locally to the EC2 instance
    • echo 'test' > mytestfile.txt
  3. Test copying a file to the S3 bucket
    • aws s3 cp mytestfile.txt s3://mybucket2share/
  4. Test listing the contents of the bucket to confirm copy action was successful
    • aws s3 ls s3://mybucket2share/

Troubleshooting

Issue Possible Cause Solution
"Access Denied" when accessing bucket from Account B Bucket policy doesn't include the role ARN or typo in ARN Verify the bucket policy Principal ARN exactly matches the role ARN in Account B. Check for typos in account ID or role name.
EC2 instance cannot assume the role Instance profile not created or not attached Create an instance profile with the same name as the role: aws iam create-instance-profile and aws iam add-role-to-instance-profile.
Can list objects but cannot download Bucket policy missing object-level permissions Ensure the bucket policy Resource includes both the bucket ARN and bucket ARN with /* for object-level access.
"InvalidIdentityToken" error Role trust policy incorrect For EC2 roles, ensure the trust policy allows ec2.amazonaws.com as the Principal Service with sts:AssumeRole action.
Permissions work inconsistently IAM policy and bucket policy conflicting Both the IAM policy in Account B AND the bucket policy in Account A must allow the action. Check both policies for the specific operations needed.

Want Help With This?

If you're working on something similar and want a second set of eyes, or you'd like to talk through how this applies to your environment, reach out via the contact form. Happy to help.

Related Articles