Fix: AWS AccessDeniedException when calling an AWS service operation
Quick Answer
How to fix AWS AccessDeniedException caused by missing IAM permissions, explicit denies, SCPs, resource policies, permission boundaries, and misconfigured roles.
The Error
You call an AWS API and get:
An error occurred (AccessDeniedException) when calling the DescribeInstances operation:
User: arn:aws:iam::123456789012:user/myuser is not authorized to perform: ec2:DescribeInstancesOr variations like:
An error occurred (AccessDenied) when calling the PutObject operation: Access DeniedAn error occurred (UnauthorizedAccess) when calling the AssumeRole operation:
User: arn:aws:sts::123456789012:assumed-role/my-role/session is not authorized to perform: sts:AssumeRolebotocore.exceptions.ClientError: An error occurred (AccessDeniedException) when calling the Invoke operationAWS refused your request because the identity making the call does not have permission to perform the action on the target resource.
Why This Happens
AWS uses IAM (Identity and Access Management) to control who can do what. Every API call is evaluated against IAM policies. A request is allowed only if there is an explicit Allow and no explicit Deny.
The most common causes:
- Missing IAM policy. Your user or role does not have a policy granting the required permission.
- Explicit deny. A policy explicitly denies the action, overriding any allows.
- Service Control Policy (SCP). An organization-level policy restricts the action for the entire account.
- Resource-based policy. The target resource (S3 bucket, SQS queue, Lambda function) has a policy that blocks your identity.
- Permission boundary. The IAM user or role has a permission boundary that limits its effective permissions.
- Wrong identity. You are authenticated as a different user or role than you expect.
- Cross-account access. You are trying to access a resource in a different AWS account without proper trust configuration.
- Condition key mismatch. The policy allows the action but only under specific conditions (IP range, MFA, tags) that your request doesn’t meet.
Fix 1: Verify Your Identity
Before debugging policies, confirm who AWS thinks you are:
aws sts get-caller-identityOutput:
{
"UserId": "AIDAEXAMPLEUSERID",
"Account": "123456789012",
"Arn": "arn:aws:iam::123456789012:user/myuser"
}Check:
- Is this the right user/role? You might be using the wrong AWS profile or credentials.
- Is this the right account? You might be in a sandbox or staging account.
- Is this an assumed role? If the ARN shows
assumed-role, you are using temporary credentials from an assumed role, which may have different permissions than your IAM user.
If the identity is wrong, fix your credentials. See Fix: AWS Unable to locate credentials for troubleshooting credential configuration.
Pro Tip: Use named AWS profiles to avoid accidentally using the wrong credentials:
aws sts get-caller-identity --profile production aws s3 ls --profile stagingSet
AWS_PROFILEin your shell to change the default, rather than hardcoding credentials.
Fix 2: Check and Add Missing Permissions
Find out which permission you need. The error message usually tells you:
is not authorized to perform: ec2:DescribeInstancesThis means you need the ec2:DescribeInstances permission. Add it to your IAM user or role.
Using the AWS Console:
- Go to IAM → Users (or Roles) → Select your identity
- Click “Add permissions” → “Attach policies directly”
- Search for the relevant managed policy (e.g.,
AmazonEC2ReadOnlyAccess) - Attach it
Using the AWS CLI:
aws iam attach-user-policy \
--user-name myuser \
--policy-arn arn:aws:iam::aws:policy/AmazonEC2ReadOnlyAccessUsing a custom inline policy:
aws iam put-user-policy \
--user-name myuser \
--policy-name EC2DescribeAccess \
--policy-document '{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Action": "ec2:DescribeInstances",
"Resource": "*"
}]
}'Note: For production, prefer managed policies or shared custom policies over inline policies. Inline policies are harder to audit and manage at scale.
Fix 3: Check for Explicit Denies
An explicit Deny in any policy always wins over Allow. Check all policies attached to your identity:
aws iam list-attached-user-policies --user-name myuser
aws iam list-user-policies --user-name myuser
aws iam list-groups-for-user --user-name myuserFor roles:
aws iam list-attached-role-policies --role-name my-role
aws iam list-role-policies --role-name my-roleDownload and inspect each policy. Look for "Effect": "Deny" statements:
{
"Effect": "Deny",
"Action": "ec2:*",
"Resource": "*",
"Condition": {
"StringNotEquals": {
"aws:RequestedRegion": "us-east-1"
}
}
}This policy denies all EC2 actions outside us-east-1. Even if another policy allows ec2:DescribeInstances, the deny overrides it.
Common Mistake: Adding more
Allowpolicies when an explicitDenyexists. The deny always wins. You must remove or modify the deny policy, not add more allows.
Fix 4: Use the IAM Policy Simulator
The IAM Policy Simulator evaluates your policies without making real API calls:
- Go to https://policysim.aws.amazon.com
- Select the user or role
- Choose the service and action
- Click “Run Simulation”
The simulator tells you whether the action is allowed or denied and which policy is responsible.
From the CLI:
aws iam simulate-principal-policy \
--policy-source-arn arn:aws:iam::123456789012:user/myuser \
--action-names ec2:DescribeInstancesThis shows which policies allow or deny the action and why.
Fix 5: Check Service Control Policies (SCPs)
If your AWS account is part of an AWS Organization, SCPs can restrict permissions at the account or OU (Organizational Unit) level. SCPs act as a guardrail — even if your IAM policy allows an action, the SCP can block it.
Check with your organization administrator. SCPs are managed at the organization level and are not visible to individual account users.
Common SCP restrictions:
- Region restrictions: Only allow actions in specific regions
- Service restrictions: Block access to certain services entirely
- Root user restrictions: Prevent root from performing certain actions
Fix 6: Check Resource-Based Policies
Some AWS resources have their own policies that control access independently of IAM:
- S3 bucket policies
- SQS queue policies
- SNS topic policies
- Lambda function policies
- KMS key policies
For S3 AccessDenied errors specifically, see Fix: AWS S3 Access Denied.
Check the resource policy:
aws s3api get-bucket-policy --bucket my-bucketA bucket policy might explicitly deny access:
{
"Effect": "Deny",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::my-bucket/*",
"Condition": {
"StringNotEquals": {
"aws:PrincipalOrgID": "o-12345example"
}
}
}This denies access to anyone outside the organization, even if IAM policies allow it.
Fix 7: Check Permission Boundaries
Permission boundaries are IAM policies attached to users or roles that set the maximum permissions. Even if an attached policy allows an action, the permission boundary must also allow it.
Check if a permission boundary is set:
aws iam get-user --user-name myuserLook for the PermissionsBoundary field in the output. If present, the effective permissions are the intersection of the attached policies and the permission boundary.
Fix 8: Fix Assume Role Issues
If the error happens when assuming a role, check the role’s trust policy:
aws iam get-role --role-name my-role --query 'Role.AssumeRolePolicyDocument'The trust policy must explicitly allow your identity to assume the role:
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::123456789012:user/myuser"
},
"Action": "sts:AssumeRole"
}]
}For cross-account role assumption, both sides need configuration:
- The role’s trust policy in the target account must trust the source account.
- The user/role in the source account must have
sts:AssumeRolepermission for the target role.
Fix 9: Debug with CloudTrail
CloudTrail logs every API call and its authorization result. Find the denied request:
aws cloudtrail lookup-events \
--lookup-attributes AttributeKey=EventName,AttributeValue=DescribeInstances \
--max-results 5Or search in CloudTrail Event History in the AWS Console. Filter by:
- Event name: The API action
- Error code:
AccessDeniedorUnauthorizedAccess - User name: Your identity
The CloudTrail event shows the exact ARN that made the request, the resource targeted, and the error. This is the definitive source for debugging access issues.
For SSL/TLS errors that prevent API calls from reaching AWS at all, see Fix: SSL certificate problem.
Still Not Working?
If you have checked everything above and still get AccessDeniedException:
Check VPC endpoints. If your service uses a VPC endpoint, the endpoint’s policy might restrict which IAM identities can use it. Check the endpoint policy in the VPC console.
Check condition keys. Some policies use conditions that aren’t obvious:
aws:SourceIp— only allows access from specific IPsaws:MultiFactorAuthPresent— requires MFAaws:PrincipalTag— requires specific tags on the identityaws:RequestedRegion— restricts to specific regionsaws:CalledVia— restricts which service can make the call
Check for recently created policies. IAM policy changes can take a few seconds to propagate. If you just added a policy, wait 10-30 seconds and retry.
Check for service-linked roles. Some AWS services require service-linked roles that must be created separately. If a service can’t create the role automatically, you get AccessDeniedException.
Check Terraform state locks. If the AccessDeniedException happens during Terraform operations, it might be related to state lock issues rather than IAM.
Check the AWS service quotas. Some services return AccessDeniedException when you hit service quotas instead of a quota-specific error. Check Service Quotas in the AWS Console.
Use the --debug flag for detailed request information:
aws ec2 describe-instances --debug 2>&1 | tail -50This shows the exact request, headers, and response, including any error details not shown in the standard output.
If network connectivity itself is the problem rather than permissions, see Fix: curl failed to connect for troubleshooting network issues to AWS endpoints.
Solo developer based in Japan. Every solution is cross-referenced with official documentation and tested before publishing.
Was this article helpful?
Related Articles
Fix: AWS CloudFormation stack in ROLLBACK_COMPLETE or CREATE_FAILED state
How to fix AWS CloudFormation ROLLBACK_COMPLETE and CREATE_FAILED errors caused by IAM permissions, resource limits, invalid parameters, and dependency failures.
Fix: AWS Lambda Task timed out after X seconds
How to fix AWS Lambda timeout errors caused by low timeout settings, cold starts, slow external API calls, VPC configuration, and unoptimized code.
Fix: Terraform Error locking state: Error acquiring the state lock
How to fix Terraform state lock error caused by concurrent runs, crashed operations, DynamoDB lock table issues, and stale lock IDs.
Fix: AWS S3 Access Denied (403 Forbidden) when uploading, downloading, or listing
How to fix the 'Access Denied' (403 Forbidden) error in AWS S3 when uploading, downloading, listing, or managing objects using the CLI, SDK, or console.