AccessDenied
AWS AccessDenied usually means AWS authenticated the caller, then denied a specific action on a specific ARN after evaluating IAM, SCP, trust, boundary, session, or resource policy layers.
Last reviewed: April 27, 2026|Source-backed guidance under our editorial policy
Start Here
Use the closest compare guide, playbook, or adjacent error page to narrow the decision faster before you start changing production systems.
This page is part of the Error Reference library. Learn more about the project or report a correction.
What Does Access Denied Mean?
In production, AccessDenied is the signal that identity proof succeeded but authorization did not. The shortest path to a fix is to identify the exact principal, action, resource ARN, and policy layer that produced the deny, then retest the same request after a minimal change.
Common Causes
- -The effective principal is different from the one operators expect (example: pipeline runs under an assumed role session from another account or stage).
- -No identity-based allow statement grants the exact action on the exact ARN, often because the resource path is narrower than the wildcard in the mental model.
- -An explicit deny exists in an SCP, permissions boundary, session policy, VPC endpoint policy, bucket policy, or KMS key policy and overrides the apparent allow.
- -Cross-account access is only half-configured: the caller can assume a role, but the target resource policy or KMS key policy does not trust that path.
- -A condition key such as
aws:PrincipalOrgID,aws:SourceVpce,s3:prefix, or session tags evaluates false for the live request.
How to Fix Access Denied
- 1Capture the exact denied principal ARN, AWS service action, full resource ARN, request ID, and region from the failing response or CloudTrail event.
- 2Run
sts get-caller-identityin the same runtime path before editing policies so you know who AWS actually evaluated. - 3Trace the deny from the outside in: SCP or boundary first, then session policy, then identity policy, then resource or key policy for the exact ARN.
- 4Make the smallest policy change that allows the precise action-resource pair, then replay the same request with the same principal.
Step-by-Step Diagnosis for Access Denied
- 1Pull the CloudTrail management or data event for the denial and confirm the live principal, service, action, resource, source IP, and additionalEventData fields.
- 2Use IAM Policy Simulator or the AWSSupport AccessDenied runbook with the same principal, action, and ARN to see which statements allow or deny the request.
- 3Check organization-level SCPs, permission boundaries, and session policies before editing inline or managed IAM role policies.
- 4For S3, KMS, SQS, SNS, and other resource-policy-aware services, inspect the target resource policy or key policy along with caller-side IAM.
- 5For assume-role paths, validate trust policy, external ID, session tags, and session duration assumptions together rather than separately.
- 6Retest the unchanged request and confirm the authorization decision changes without accidentally broadening adjacent permissions.
Seen in Production
- -S3 deploy pipeline fails with
User: arn:aws:sts::111122223333:assumed-role/deploy-role/GitHubActions is not authorized to perform: s3:PutObject on resource: arn:aws:s3:::prod-artifacts/app.zip because no identity-based policy allows the s3:PutObject action. - -KMS-backed upload fails even after S3 permissions look correct because the real deny is
kms:Encrypton the CMK, nots3:PutObjecton the bucket path. - -Terraform works in one account but not another because an org SCP introduces an explicit deny that never appears in the local role policy diff.
Identity and Session Reality Check
- -Verify the evaluated principal with
sts:GetCallerIdentityand compare it to the principal operators think is running (example: workload uses a stale profile or an inherited assumed role session). - -Inspect session tags, external ID, MFA requirements, and issuer ARN assumptions when the trust policy path looks correct but conditions still fail.
Policy Layer Decision Trace
- -Audit deny precedence across SCP, permissions boundary, session policy, identity policy, VPC endpoint policy, and resource policy before changing any one layer in isolation.
- -Confirm the target ARN is exact and not just visually similar (example: object path requires
arn:aws:s3:::prod-logs/app/*, not onlyarn:aws:s3:::prod-logs).
Wrong Fix to Avoid
- -Do not attach
AdministratorAccessjust to make the error disappear; first prove which policy layer blocked the exact action-resource pair. - -Do not stop at the first apparent allow statement; explicit denies and resource policies can still win even when IAM role JSON looks correct.
Implementation Examples
{
"eventSource": "s3.amazonaws.com",
"eventName": "PutObject",
"awsRegion": "us-east-1",
"userIdentity": {
"type": "AssumedRole",
"arn": "arn:aws:sts::111122223333:assumed-role/deploy-role/GitHubActions"
},
"requestParameters": {
"bucketName": "prod-artifacts",
"key": "releases/app.zip"
},
"errorCode": "AccessDenied",
"errorMessage": "User is not authorized to perform s3:PutObject on arn:aws:s3:::prod-artifacts/releases/app.zip"
}aws sts get-caller-identity
aws iam simulate-principal-policy \
--policy-source-arn arn:aws:iam::111122223333:role/deploy-role \
--action-names s3:PutObject \
--resource-arns arn:aws:s3:::prod-artifacts/releases/app.zip{
"eventSource": "kms.amazonaws.com",
"eventName": "GenerateDataKey",
"awsRegion": "us-east-1",
"userIdentity": {
"type": "AssumedRole",
"arn": "arn:aws:sts::111122223333:assumed-role/deploy-role/GitHubActions"
},
"resources": [
{
"ARN": "arn:aws:kms:us-east-1:111122223333:key/1234abcd-56ef-78ab-90cd-1234567890ef"
}
],
"errorCode": "AccessDenied",
"errorMessage": "User is not authorized to perform kms:GenerateDataKey on the CMK used by the target bucket"
}Incident Timeline
10:02 UTC
CI assumes the expected deploy role successfully
Signal: The pipeline can call sts:AssumeRole, so operators initially think identity and authorization are both healthy.
Why it matters: This narrows the failure from authentication to a later authorization hop. The remaining deny is often on the target resource or KMS key path, not on the assume-role step itself.
10:03 UTC
Artifact upload fails on the real write path
Signal: CloudTrail shows s3:PutObject denied for arn:aws:s3:::prod-artifacts/releases/app.zip under the live assumed-role session.
Why it matters: At this point you need the exact principal, action, and ARN tuple before editing policies. Guessing from the console role view is usually slower than tracing the concrete denial event.
10:05 UTC
A second deny appears one layer deeper
Signal: The bucket policy looks correct, but a related event reveals kms:GenerateDataKey or kms:Encrypt is also denied for the same workflow.
Why it matters: AccessDenied often hides a multi-layer path. Fixing only the outer S3 permission still leaves the KMS or resource-policy deny untouched.
10:12 UTC
The least-privilege exception is replayed in the same runtime
Signal: After updating the blocking policy layer for the exact principal and ARN, the same CI job succeeds without widening adjacent permissions.
Why it matters: The win condition is not “the console policy looks nicer”; it is “the same principal, same action, same resource now passes and nearby scope stays constrained.”
Seen in Production
Cross-account deploy role can assume target role but still fails on the real resource
Frequency: common
Example: The assume-role step succeeds, then s3:PutObject or kms:Encrypt fails because the resource or key policy does not trust the session path.
Fix: Validate both the caller-side identity policy and the target-side resource or key policy for the exact ARN used in the failing step.
Org guardrail breaks a previously healthy workflow overnight
Frequency: medium
Example: A new SCP or permissions boundary introduces an explicit deny, while the role policy in the application repo remains unchanged.
Fix: Trace policy evaluation order from org boundary down to the resource policy, then adjust the blocking guardrail or approved exception path.
Wrong caller identity in CI or container runtime
Frequency: common
Example: Engineers inspect one IAM role in the console, but the live workload actually runs under another role session selected by profile, task, or node identity.
Fix: Capture sts:GetCallerIdentity and session issuer details from the failing runtime before editing any policies.
Wrong Fix vs Better Fix
Broad admin grant vs exact policy decision trace
Wrong fix: Attach AdministratorAccess or a broad Action: "*" / Resource: "*" statement just to clear the incident quickly.
Better fix: Simulate and trace the exact denied principal-action-resource tuple, then change only the blocking SCP, boundary, identity, resource, or key policy statement.
Why this is better: The incident closes without normalizing permanent over-permission, and future regressions stay explainable because the deny layer is known.
Editing only IAM role JSON vs tracing every policy layer
Wrong fix: Stop after the first apparent allow statement in the role policy and assume the rest of the stack is fine.
Better fix: Check deny precedence across SCP, permissions boundary, session policy, VPC endpoint policy, resource policy, and KMS key policy for the exact ARN.
Why this is better: AccessDenied is frequently caused by a different layer than the one engineers are staring at. Following evaluation order is faster than patching one JSON document at a time.
Testing from a local admin shell vs replaying the live runtime path
Wrong fix: Validate the fix from your local admin credentials or a different profile and assume production will behave the same way.
Better fix: Run sts get-caller-identity and replay the request inside the same CI runner, task role, or container path that originally failed.
Why this is better: The live principal is often not the one operators expect. Replaying in the original runtime closes the exact authorization gap instead of proving a different identity works.
Debugging Tools
- -CloudTrail management and data events
- -
sts get-caller-identityin the same runtime environment - -IAM Policy Simulator
- -AWS Access Analyzer
- -AWS CLI
--debugtraces - -AWSSupport-TroubleshootIAMAccessDeniedEvents runbook
How to Verify the Fix
- -Replay the exact denied request with the same principal, region, session context, and resource ARN and confirm the operation succeeds.
- -Confirm CloudTrail for the retried request no longer shows
errorCode=AccessDeniedfor that action-resource tuple or its adjacent KMS/resource-policy path. - -Run IAM Policy Simulator or Access Analyzer again for the corrected tuple and verify the allow comes from the intended statement rather than a broader fallback grant.
- -Run a least-privilege smoke test on nearby actions and sibling resources so the fix does not silently overgrant access.
- -If the path is cross-account, validate both the caller-side assume-role step and the target resource action now work in sequence.
How to Prevent Recurrence
- -Add policy-as-code checks that test the exact high-risk action-resource pairs your deploy and runtime paths require.
- -Standardize trust policy, session tag, and condition-key conventions across accounts so cross-account flows do not drift by team.
- -Alert on repeated AccessDenied spikes by principal, action, and resource family in CloudTrail analytics.
Pro Tip
- -persist
principal + action + resource + requestIdfor every denial so future regressions can be mapped to one concrete policy decision instead of generic access debugging.
Decision Support
Compare Guide
AWS AccessDenied vs GCP PERMISSION_DENIED (403)
Compare AWS AccessDenied and GCP PERMISSION_DENIED to isolate authorization deny layers, separate auth failures, and apply precise IAM fixes fast.
Playbook
Authorization Denial Playbook (403 / AccessDenied / PERMISSION_DENIED)
Use this playbook to triage policy-based access denials after authentication succeeds, isolate the deny layer, and apply least-privilege remediation safely.
Playbook
CORS Error Fix Playbook (Preflight / Origin / Credentials)
Use this playbook to separate browser-enforced cross-origin policy failures from server-side CORS header and route defects and apply strict origin and credential controls safely.
Official References
Provider Context
This guidance is specific to AWS services. Always validate implementation details against official provider documentation before deploying to production.