When your application—say, running on an EC2 instance—needs to interact with your data in Amazon S3 buckets, then the secure, and best practice approach to access “s3” is using IAM Roles. These IAM Roles provide temporary credentials that your service (the trusted entity) can assume, ensuring your secrets are never exposed or permanently stored. This post walks you through creating an IAM Role for S3 access, focusing on the critical Principle of Least Privilege by showing you how to restrict EC2 access to a single bucket, multiple buckets, or all buckets.
Contents
- Step 1: Create the IAM Role and Define the Trusted Entity
- Step 2: Define S3 Permissions with the Policy
- Step 3: Attach the IAM Role to Your EC2 Instance
Step 1: Create the IAM Role and Define the Trusted Entity
The IAM Role defines who can access the resource and what they can do.
- Create an IAM Role: In the AWS Management Console, navigate to IAM and select Roles, then click Create role.
- Select EC2 as the Trusted Entity: When asked for the trusted entity, choose AWS service, and then select EC2 from the list of use cases. This establishes a Trust Policy, allowing an EC2 instance to assume this role.
Step 2: Define S3 Permissions with the Policy
This is where you determine exactly which S3 buckets the role can touch. After selecting your trusted entity (EC2), you will attach a Permissions Policy. You have three main options for defining the scope:
Option A: Access to a Single S3 Bucket (Least Privilege)
This is the most secure and recommended approach. It strictly limits the role’s blast radius to only the single S3 bucket it needs.
- Policy Type: Custom Policy (JSON)
- Permissions: You only include the specific actions needed (e.g.,
s3:GetObject
to read,s3:PutObject
to write). - Resource ARN: Use the Amazon Resource Name (ARN) of the specific bucket. You must include the bucket ARN (
arn:aws:s3:::your-single-bucket-name
) for bucket-level actions and an object ARN (arn:aws:s3:::your-single-bucket-name/*
) for object-level actions.
Action | Bucket Policy ARN | Object Policy ARN |
List/Get Location | arn:aws:s3:::your-single-bucket-name | N/A |
Read/Write Objects | N/A | arn:aws:s3:::your-single-bucket-name/* |
Example JSON Policy for Read/Write on a Single Bucket:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowBucketList",
"Effect": "Allow",
"Action": [
"s3:ListBucket",
"s3:GetBucketLocation"
],
"Resource": "arn:aws:s3:::my-secure-logs-bucket"
},
{
"Sid": "AllowObjectRW",
"Effect": "Allow",
"Action": [
"s3:GetObject",
"s3:PutObject",
"s3:DeleteObject"
],
"Resource": "arn:aws:s3:::my-secure-logs-bucket/*"
}
]
}
Option B: Access to Multiple Specific S3 Buckets
If your service needs access to several known buckets, you simply list all of their ARNs in the Resource
block.
- Policy Type: Custom Policy (JSON)
- Resource ARNs: Include the ARN for each specific bucket and its objects.
Example JSON Snippet (List Multiple Buckets):
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowBucketList",
"Effect": "Allow",
"Action": [
"s3:ListBucket",
"s3:GetBucketLocation"
],
"Resource": [
"arn:aws:s3:::bucket-for-app-data",
"arn:aws:s3:::bucket-for-app-data/*",
"arn:aws:s3:::bucket-for-shared-assets",
"arn:aws:s3:::bucket-for-shared-assets/*"
]
},
{
"Sid": "AllowObjectRW",
"Effect": "Allow",
"Action": [
"s3:GetObject",
"s3:PutObject",
"s3:DeleteObject"
],
"Resource": [
"arn:aws:s3:::bucket-for-app-data",
"arn:aws:s3:::bucket-for-app-data/*",
"arn:aws:s3:::bucket-for-shared-assets",
"arn:aws:s3:::bucket-for-shared-assets/*"
]
}
]
}
Option C: Access to All S3 Buckets in the Account
This is the least restrictive option. It is typically used for administrative, backup, or logging roles where the service truly needs broad access across the account.
- Policy Type: AWS Managed Policy
AmazonS3FullAccess
(or a custom policy usings3:*
and*
for the resource). - Resource ARN: The
Resource
value is set to a wildcard ("*"
) for all S3 resources.
Example Policy Snippet (All Buckets):
{
"Effect": "Allow",
"Action": "s3:*",
"Resource": "*"
}
Security Note: While convenient, granting
s3:*
on all resources violates the Principle of Least Privilege and should be avoided unless absolutely necessary for administrative tasks.
Step 3: Attach the IAM Role to Your EC2 Instance
Once the policy is created and named, you must link the role (which now has the policy) to the EC2 instance (which can assume the role).
- Go to your EC2 instance dashboard in the specific region of your choice
- Select the instance, then navigate to Actions → Security → Modify IAM role.
- Select the Role you just created from the dropdown and Save.
That’s it! Your EC2 instance is now authorized. Any application or script on that instance (like the AWS CLI or using boto3) can now automatically assume the role and perform the S3 operations defined in your policy!
Verify Access Using the AWS CLI
From your EC2 instance, you can test the access immediately using the AWS CLI:
# This command should succeed if the role is attached and has s3:ListBucket
aws s3 ls
# This command will test object-level access (read/write)
aws s3 cp my-file.txt s3://my-secure-logs-bucket/
By leveraging IAM Roles, you can securly connect to an S3 bucket from an EC2 instance without compromising by using access keys for an role!