Fix AWS S3 'Access Denied' Errors
The Error: "Access Denied" on S3
You try to access an S3 object and get:
<Error>
<Code>AccessDenied</Code>
<Message>Access Denied</Message>
<RequestId>ABC123DEF456</RequestId>
</Error>
Or from the CLI:
An error occurred (AccessDenied) when calling the GetObject operation: Access Denied
This is one of the most common and most frustrating AWS errors because there are at least six different places where access can be blocked.
Root Cause
S3 access is evaluated across multiple policy layers. A denial in any layer blocks access:
- IAM user/role policy -- caller doesn't have
s3:GetObject - Bucket policy -- explicit deny or missing allow for the caller
- S3 Block Public Access -- account or bucket level blocks
- Object ACL -- object owned by a different account
- VPC Endpoint policy -- restricts which buckets can be accessed
- KMS key policy -- caller can't decrypt the encryption key
Step-by-Step Fix
1. Identify Who You Are
First, confirm which identity is making the request:
aws sts get-caller-identity
{
"UserId": "AROA3XFRBF23ABCDEFGH:session-name",
"Account": "123456789012",
"Arn": "arn:aws:sts::123456789012:assumed-role/my-role/session-name"
}
2. Check IAM Policy
Verify the IAM role or user has S3 permissions:
aws iam simulate-principal-policy \
--policy-source-arn arn:aws:iam::123456789012:role/my-role \
--action-names s3:GetObject \
--resource-arns arn:aws:s3:::my-bucket/my-key
If result is implicitDeny, the IAM policy doesn't grant access. Add it:
{
"Effect": "Allow",
"Action": [
"s3:GetObject",
"s3:ListBucket"
],
"Resource": [
"arn:aws:s3:::my-bucket",
"arn:aws:s3:::my-bucket/*"
]
}
Common mistake: missing the /* suffix. s3:GetObject needs bucket/*, while s3:ListBucket needs just bucket.
3. Check Bucket Policy
aws s3api get-bucket-policy --bucket my-bucket --output text | jq .
Look for explicit Deny statements. A common gotcha is a policy that denies all access except from a specific VPC:
{
"Effect": "Deny",
"Principal": "*",
"Action": "s3:*",
"Resource": "arn:aws:s3:::my-bucket/*",
"Condition": {
"StringNotEquals": {
"aws:sourceVpc": "vpc-abc123"
}
}
}
If you're calling from outside that VPC, you'll get Access Denied.
4. Check Block Public Access
aws s3api get-public-access-block --bucket my-bucket
If RestrictPublicBuckets is true, even bucket policies granting public access are ignored.
5. Check Object Ownership (Cross-Account)
If objects were uploaded by a different AWS account:
aws s3api get-object-acl --bucket my-bucket --key my-key
Fix with bucket-owner-full-control:
# Re-upload with proper ownership
aws s3 cp s3://my-bucket/my-key s3://my-bucket/my-key \
--acl bucket-owner-full-control
Or enforce ownership at the bucket level:
aws s3api put-bucket-ownership-controls --bucket my-bucket \
--ownership-controls '{"Rules":[{"ObjectOwnership":"BucketOwnerEnforced"}]}'
6. Check KMS Encryption
If the bucket uses KMS encryption, the caller needs kms:Decrypt permission on the key:
aws s3api head-object --bucket my-bucket --key my-key
# Check the ServerSideEncryption and SSEKMSKeyId fields
Add KMS permissions to the IAM policy:
{
"Effect": "Allow",
"Action": ["kms:Decrypt", "kms:GenerateDataKey"],
"Resource": "arn:aws:kms:us-east-1:123456789012:key/key-id"
}
Prevention Tips
- Use IAM Access Analyzer to verify that policies grant the intended access:
aws accessanalyzer validate-policy. - Enable S3 server access logging or CloudTrail data events to see exactly which requests are denied and why.
- Use
BucketOwnerEnforcedownership on all new buckets to eliminate ACL headaches. - Standardize on a single encryption key per environment to reduce KMS policy complexity.
- Test access with
--dryrunwhere supported, or useaws iam simulate-principal-policybefore deploying.
Was this article helpful?
CI/CD Engineering Lead
Automation evangelist who believes no deployment should require a human. I write pipelines, break pipelines, and write about both. Code-first, always.
Related Articles
AWS CLI: Cheat Sheet
AWS CLI cheat sheet with copy-paste commands for EC2, S3, IAM, Lambda, ECS, CloudFormation, SSM, and Secrets Manager operations.
AWS Core Services: The DevOps Engineer's Essential Guide
Navigate the essential AWS building blocks — EC2, S3, VPC, IAM, RDS, Lambda, and EKS explained for DevOps engineers with practical examples.
Fix AWS EC2 Instance 'Status Check Failed' Errors
Diagnose and recover AWS EC2 instances with failed system or instance status checks using stop/start, volume rescue, and auto-recovery techniques.
AWS IAM Least Privilege: Policies That Won't Lock You Out
Build AWS IAM policies using least privilege without locking yourself out — practical patterns for roles, service accounts, and permission boundaries.
AWS Lambda Cold Start Optimization Using Provisioned Concurrency And SnapStart
Cold starts are the silent SLO killer in serverless architectures. You've built a beautiful Lambda function, deployed it, and everything looks great in you...
AWS ECS Fargate: Serverless Container Deployment Without Managing Nodes
Deploy containers on AWS ECS Fargate — no EC2 instances to manage. Covers task definitions, services, load balancers, environment variables, secrets from SSM/Secrets Manager, auto-scaling, and CI/CD integration.
More in AWS
View all →AWS EKS: Production Kubernetes Cluster Setup from Scratch
Step-by-step guide to launching a production-ready EKS cluster on AWS — node groups, IAM roles, VPC configuration, managed add-ons, kubeconfig setup, and cost optimization. Both eksctl and Terraform approaches covered.
Cloud Landing Zone Architecture: Account Structure Done Right
How to design an AWS Landing Zone with Organizations, SCPs, and account vending that scales from 5 to 500 accounts without becoming a mess.