AccessDenied from AWS means the request was authenticated fine but failed authorization: some layer of policy evaluation refused the action. The frustrating part is that AWS evaluates several independent policy types, and any one of them can be the blocker. The fast path is to stop editing policies at random and instead identify which principal made the call and which layer denied it.
AccessDenied: User is not authorized to perform this operation
What this error means
Every AWS API call passes through policy evaluation. An explicit Deny anywhere wins immediately. Otherwise the request needs an Allow that survives all applicable layers: service control policies (SCPs) from AWS Organizations, the identity policy attached to the user or role, any permissions boundary on that principal, the session policy if the credentials came from sts:AssumeRole, and the resource policy on the target (S3 bucket policy, KMS key policy, SQS queue policy, and so on). Cross account access needs an Allow on both sides. AccessDenied means this evaluation ended in a deny, and the fix is locating which layer produced it.
Common causes
- The identity policy simply lacks the action or scopes it to the wrong resource ARN (a common trap in S3:
s3:ListBucketneeds the bucket ARN,s3:GetObjectneedsarn:aws:s3:::bucket/*). - An explicit Deny in an SCP, often a region restriction or a guardrail added by the platform team.
- A permissions boundary on the role that does not include the action, so the identity Allow is capped.
- The wrong principal is making the call: an EC2 instance profile, a Lambda execution role, or a stale CLI profile instead of the identity you tested.
- A resource policy that does not grant the caller, which matters for cross account S3, KMS, and Lambda invocations.
- A Condition that does not match, such as
aws:SourceVpce,aws:PrincipalOrgID, MFA requirements, or an IP allowlist. - KMS: the data operation is allowed but the object is encrypted with a key whose key policy excludes the caller, surfacing as AccessDenied on the S3 call.
How to fix it
STABILIZE first. If a production workload is blocked, confirm whether a recent policy change caused it and roll that change back while you investigate, rather than widening permissions under pressure.
- DIAGNOSE who actually made the call. Verify the principal from the failing environment itself:
aws sts get-caller-identity
- Find the denied event in CloudTrail and read its details, including the userIdentity and error message:
aws cloudtrail lookup-events \
--lookup-attributes AttributeKey=EventName,AttributeValue=GetObject \
--max-results 20 \
--query 'Events[?contains(CloudTrailEvent, `AccessDenied`)].CloudTrailEvent'
- Simulate the evaluation for that principal and action to see which statement fails:
aws iam simulate-principal-policy \
--policy-source-arn arn:aws:iam::123456789012:role/app-role \
--action-names s3:GetObject \
--resource-arns arn:aws:s3:::my-bucket/reports/2026.csv
- FIX the layer that denied. If the identity policy is the gap, add a minimal statement scoped to the exact resource:
aws iam put-role-policy \
--role-name app-role \
--policy-name allow-reports-read \
--policy-document '{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Action": ["s3:GetObject"],
"Resource": "arn:aws:s3:::my-bucket/reports/*"
}]
}'
- If the simulator allows but reality denies, the blocker is a layer the simulator did not model in your run: check SCPs with your organization admin, the permissions boundary on the role, the resource policy on the target, and the key policy for any KMS encrypted resource.
How to prevent it
- Grant permissions to roles, not users, and keep one policy per workload so a denial has one obvious place to look.
- Use IAM Access Analyzer to validate policies before deploying and to flag unused or unintended access.
- Test policy changes in a staging account under the same SCPs and boundaries as production.
- Keep CloudTrail on in every account so every denial is attributable to a principal and a policy.
- Document which SCPs and boundaries apply per account, since these are the layers application teams cannot see in their own console.
When to call a senior engineer
Call for help when the denial spans accounts or organizations and nobody owns the full policy picture, when KMS key policies are involved and the wrong edit could lock data away, or when unblocking production keeps turning into wildcard permissions nobody dares remove. Erzon engineers can trace the exact denying statement across every evaluation layer, restore access with least privilege instead of stars, and leave the account with policies your team can actually reason about.
Related errors we fix
Stack:arn:... is in ROLLBACK_COMPLETE state and can not be updated. Fix this error → AWS UPDATE_ROLLBACK_FAILED Fix this error → AWS ProvisionedThroughputExceededException: The level of configured provisioned throughput for the table was exceeded Fix this error → AWS InsufficientInstanceCapacity Fix this error → Want a senior engineer to just fix it?
Paste this exact error into the form. A senior engineer replies within one business hour with what is likely wrong and a flat quote. Triage is free.
Book a fix