AWS IAM: AccessDenied on sts:AssumeRole — Both Sides of the Trust

Logeshwaran.C

The most common cause of an AccessDenied error when assuming an IAM role is a mismatch between the trust policy of the target role and the permissions of the identity doing the assuming. Whether you're using the AWS STS AssumeRole API, the AWS CLI assume-role command, or the AWS Management Console, the failure almost always traces back to one of six specific configuration issues. This guide covers each one with exact commands and policy JSON to fix it. We have been covering AWS series, if you are new and non tech, don't worry you can start reading, we have a dedicated hub "Learn AWS for free" for this. 

⚡ Quick Answer

Run this firstaws sts assume-role --role-arn arn:aws:iam::ACCOUNT2:role/ROLE_NAME --role-session-name "DebugSession"

If that works → Your CLI profile is the problem. Check ~/.aws/credentials and ~/.aws/config for typos in source_profile or role_arn.

If that fails → The trust policy is blocking you. Verify the Principal (your account/user ARN) and any Conditions like MFA.

For the full diagnostic path, follow the flowchart below. If you're new to IAM entirely, start with what AWS IAM cloud permissions actually mean.

Understanding IAM Roles, Trust Policies, and the Service Principal

IAM roles are AWS identities with specific permissions that can be assumed by trusted entities. The trust policy defines who can assume the role (the "who" side), while the permission policy defines what the assumed role can do (the "what" side). An AccessDenied error during sts:AssumeRole means one of these two policies is not configured to allow the action.

The "who" side includes IAM users in AWS, IAM roles, and AWS service principals. A service principal is the identity an AWS service uses when it needs to act on your behalf—like EC2 launching with an instance profile, or Lambda running your function. Each of these can be a Principal in a trust policy, and getting the principal type wrong is a top cause of AccessDenied errors.

‍♂️ Jake's Reality Check

"I set up the role exactly like the tutorial said, and the policy allows sts:AssumeRole. Why is AWS telling me I'm not authorized?"

Ethan's take: Because there are two sides to this coin, Jake. The tutorial probably showed you the permission policy on the role, but not the trust policy that controls who can actually use it. It's like installing a deadbolt on your front door but leaving the door unlocked—anyone can walk in.

AWS STS AssumeRole API: Six Causes of AccessDenied

Cause Where to Look Primary Fix
1. Trust Policy Principal Mismatch Role's Trust Policy Correct the Principal AWS ARN
2. IAM User Policy Missing Permission IAM User's Attached Policy Add sts:AssumeRole with correct Resource
3. MFA Condition Not Met CLI Command or Trust Policy Provide MFA token or remove condition
4. External ID Mismatch AssumeRole API Call Pass the correct ExternalId
5. CLI Profile Misconfiguration ~/.aws/credentials and ~/.aws/config Fix source_profile or role_arn typos
6. Role Assumption from an Assumed Role Source Policy's Trust Chain Ensure source role has sts:AssumeRole permission

1. The Trust Policy Principal Doesn't Match Your Identity (Including Service Principals)

The trust policy's Principal element specifies which AWS identities are allowed to assume the role. If this doesn't match your identity's ARN (Amazon Resource Name), you'll get AccessDenied. This applies to AWS IAM service principals too—if EC2 or Lambda can't assume a role, the service principal in the trust policy is wrong.

  1. Open the IAM Role in the AWS Console and view its Trust relationships tab.
  2. Check the Principal:
    • For an IAM user: "Principal": {"AWS": "arn:aws:iam::ACCOUNT1:user/USERNAME"}
    • For an entire account: "AWS": "arn:aws:iam::ACCOUNT1:root"
    • For a service principal: "Service": "ec2.amazonaws.com" or "Service": "lambda.amazonaws.com"
  3. Verify your ARN:
    • For your user: aws sts get-caller-identity (look for the Arn field)
    • For your account: aws sts get-caller-identity --query Account --output text

⚠︇ What this actually breaks

Using the root principal (arn:aws:iam::ACCOUNT1:root) allows any identity in that account to assume the role—including IAM users you might not want to have access. Always scope down to specific users or roles when possible.

2. The IAM User Lacks sts:AssumeRole Permission

The identity trying to assume the role must have an attached policy that explicitly allows the sts:AssumeRole action on the specific role ARN.

  1. Check the IAM User's Policies:
    • Inline policies: aws iam list-user-policies --user-name USERNAME
    • Attached managed policies: aws iam list-attached-user-policies --user-name USERNAME
  2. Verify the Policy Statement should include:
    {
      "Effect": "Allow",
      "Action": "sts:AssumeRole",
      "Resource": "arn:aws:iam::ACCOUNT2:role/ROLE_NAME"
    }
  3. Add the Permission if Missing:
    aws iam put-user-policy \
      --user-name USERNAME \
      --policy-name AssumeRolePermission \
      --policy-document '{
        "Version": "2012-10-17",
        "Statement": [{
          "Effect": "Allow",
          "Action": "sts:AssumeRole",
          "Resource": "arn:aws:iam::ACCOUNT2:role/ROLE_NAME"
        }]
      }'

3. MFA is Required but Not Being Provided

If the trust policy includes a condition like "Bool": {"aws:MultiFactorAuthPresent": "true"}, you must provide an MFA token when assuming the role.

The two common scenarios:

  • Using a CLI profile with MFA configured: Your ~/.aws/credentials file must include the mfa_serial and token_code (the one-time code). AWS CLI will prompt for the code if not hardcoded.
  • Using the direct API call: You must explicitly pass the --serial-number and --token-code parameters 
aws sts assume-role \
  --role-arn arn:aws:iam::ACCOUNT2:role/ROLE_NAME \
  --role-session-name "MFASession" \
  --serial-number arn:aws:iam::ACCOUNT1:mfa/USERNAME \
  --token-code 123456

4. The External ID Doesn't Match

When assuming a role from a third-party account or service, AWS uses an ExternalId as an additional security check. If this doesn't match what's in the trust policy, you'll get AccessDenied.

  1. Check if the trust policy requires an ExternalId:
    "Condition": {
      "StringEquals": {
        "sts:ExternalId": "SPECIFIC_EXTERNAL_ID"
      }
    }
  2. Pass the ExternalId when assuming the role:
    aws sts assume-role \
      --role-arn arn:aws:iam::ACCOUNT2:role/ROLE_NAME \
      --role-session-name "ExternalIdSession" \
      --external-id SPECIFIC_EXTERNAL_ID

5. The AWS CLI Profile is Misconfigured

The AWS CLI uses profiles in ~/.aws/credentials and ~/.aws/config to manage role assumption. A typo here is the most common cause of AccessDenied.

  1. Check your credentials file (~/.aws/credentials):
    [default]
    aws_access_key_id = YOUR_ACCESS_KEY
    aws_secret_access_key = YOUR_SECRET_KEY
    
    [profile-name]
    role_arn = arn:aws:iam::ACCOUNT2:role/ROLE_NAME
    source_profile = default
    mfa_serial = arn:aws:iam::ACCOUNT1:mfa/USERNAME
  2. Check your config file (~/.aws/config):
    [profile profile-name]
    region = us-east-1
    output = json
  3. Verify the chain: The source_profile must match a valid section in the credentials file that has the actual access keys.

6. Assuming a Role from an Already-Assumed Role

If you're already using temporary credentials from an assumed role, you might not have permission to assume another role. The source role's permission policy must explicitly allow sts:AssumeRole on the target role ARN 

  1. Check your current identity: aws sts get-caller-identity
  2. If you see an assumed-role ARN, you need to check that role's permission policy for:
    {
      "Effect": "Allow",
      "Action": "sts:AssumeRole",
      "Resource": "arn:aws:iam::ACCOUNT2:role/TARGET_ROLE_NAME"
    }

The Diagnostic Flowchart: Find Your Failure Point

Follow this path to systematically identify where the trust relationship is breaking. If you need a broader refresher on AWS IAM permissions in general, this guide on AWS IAM cloud permissions covers the basics before you dive into the specifics here.

Step What to Check Command or Action If It Fails, Look At
1 Basic CLI call aws sts assume-role --role-arn ... Your CLI credentials/profile (Cause #5)
2 IAM User Policy aws iam list-user-policies ... Missing sts:AssumeRole permission (Cause #2)
3 Trust Policy Principal aws iam get-role --role-name ... Principal mismatch (Cause #1)
4 MFA Requirements Check trust policy for MFA condition MFA not provided (Cause #3)
5 ExternalId Check trust policy for ExternalId condition ExternalId mismatch (Cause #4)
6 Current Identity aws sts get-caller-identity Assumed-role permission (Cause #6)

AWS STS AssumeRole: CLI vs Console vs API—Which to Use When

Different tools hit the same trust policy, but they surface errors differently. Here's when to use each:

Method Best For Error Clarity MFA Support
AWS CLI Scripting, automation, local development High—exact error codes and JSON output Manual token entry or profile config
AWS Console Quick manual access, role switching in browser Low—generic "You are not authorized" messages Handled automatically if MFA is on the session
Direct API Applications, SDKs, programmatic access Medium—structured error responses Must pass serial-number and token-code parameters

If you're hitting AccessDenied in the Console but the CLI works (or vice versa), the difference is almost always MFA handling or profile configuration. For more on general AWS CLI AccessDenied errors beyond AssumeRole, this guide on AWS CLI AccessDenied errors covers the broader territory.

AWS STS AssumeRole with SAML: The Federation Failure

If you're using aws sts assume-role-with-saml (for SAML federation with Okta, Azure AD/Entra ID, or another identity provider), the trust policy requirements are slightly different. The Principal must be a SAML provider, not an AWS account or user.

aws sts assume-role-with-saml \
  --role-arn arn:aws:iam::ACCOUNT2:role/ROLE_NAME \
  --principal-arn arn:aws:iam::ACCOUNT2:saml-provider/PROVIDER_NAME \
  --saml-assertion file://saml_assertion.xml

Common failure modes with SAML:

  • Principal-arn mismatch: The SAML provider ARN in your command must match the one in the trust policy.
  • Expired or invalid SAML assertion: The assertion XML must be valid and unexpired.
  • Role ARN not in the assertion: The SAML assertion must explicitly list the role ARN you're trying to assume.

Fixing the Trust Policy: The Template

Here's a trust policy that covers the most common scenarios while maintaining security best practices:

{
  "Version": "2012-10-17",
  "Statement": [{
    "Effect": "Allow",
    "Principal": {
      "AWS": [
        "arn:aws:iam::ACCOUNT1:user/USERNAME",
        "arn:aws:iam::ACCOUNT1:role/SOURCE_ROLE_NAME"
      ]
    },
    "Action": "sts:AssumeRole",
    "Condition": {
      "Bool": {
        "aws:MultiFactorAuthPresent": "true"
      },
      "StringEquals": {
        "sts:ExternalId": "your_external_id_here"
      }
    }
  }]
}

✅ Why this is the best default

This policy explicitly lists trusted identities (rather than using root), requires MFA for all human access, and uses an ExternalId for programmatic access. It's the principle of least privilege in action—only specific identities, with additional factors, can assume the role.

Fixing the Permission Policy on the Assuming Identity

If your IAM user or source role lacks permission to assume the target role, use this policy template:

{
  "Version": "2012-10-17",
  "Statement": [{
    "Effect": "Allow",
    "Action": "sts:AssumeRole",
    "Resource": [
      "arn:aws:iam::ACCOUNT2:role/ROLE_NAME"
    ]
  }]
}

Using the AWS CLI Correctly for Role Assumption

Method 1: Using a Named Profile (Recommended)

  1. Create the profile in ~/.aws/credentials:
    [profile-assume-role]
    role_arn = arn:aws:iam::ACCOUNT2:role/ROLE_NAME
    source_profile = default
    mfa_serial = arn:aws:iam::ACCOUNT1:mfa/USERNAME
  2. Use the profile:
    aws sts assume-role --profile profile-assume-role

Method 2: Direct API Call with All Parameters

aws sts assume-role \
  --role-arn arn:aws:iam::ACCOUNT2:role/ROLE_NAME \
  --role-session-name "MySession" \
  --duration-seconds 3600 \
  --external-id "your_external_id" \
  --serial-number arn:aws:iam::ACCOUNT1:mfa/USERNAME \
  --token-code 123456

The iam:PassRole Distinction: When It Applies Instead

A common source of confusion: iam:PassRole is not the same as sts:AssumeRole, and mixing them up causes AccessDenied errors that look like trust policy failures but aren't.

The difference:

  • sts:AssumeRole = "I want to become this role and use its permissions directly."
  • iam:PassRole = "I want to give this role to an AWS service (like EC2 or Lambda) so the service can assume it on my behalf."

If you're launching an EC2 instance with an instance profile, or configuring a Lambda function's execution role, you need iam:PassRole—not sts:AssumeRole. The trust policy in that scenario must allow the service principal (like ec2.amazonaws.com), not your IAM user.

{
  "Version": "2012-10-17",
  "Statement": [{
    "Effect": "Allow",
    "Action": "iam:PassRole",
    "Resource": "arn:aws:iam::ACCOUNT1:role/SERVICE_ROLE_NAME"
  }]
}

Advanced Troubleshooting: When Nothing Else Works

Check for Explicit Deny

An explicit Deny in any policy (trust or permission) overrides any Allow. Check for:

  • "Effect": "Deny" in the trust policy
  • "Effect": "Deny" in any policy attached to the assuming identity
  • Service Control Policies (SCPs) in AWS Organizations that deny the action at the account level

Verify the Role is Not Deactivated

IAM roles can be deactivated. Check with:

aws iam get-role --role-name ROLE_NAME --query 'Role.RoleLastUsed'

If RoleLastUsed is empty and the role was created more than 90 days ago, it may have been deactivated automatically.

Check CloudTrail for the Actual Denial Reason

AWS logs all IAM and STS calls to CloudTrail. Look for the AssumeRole event and check the errorCode and errorMessage fields for the precise reason:

  1. Go to CloudTrail > Event history
  2. Filter for Event source: sts.amazonaws.com
  3. Find your AssumeRole call
  4. Check the Event JSON for errorCode and errorMessage

CloudTrail is also where you catch issues that the error message doesn't spell out—like a service control policy blocking the call from an entire AWS account. If you suspect a broader security or monitoring gap, Amazon GuardDuty can detect anomalous API calls, and Amazon Inspector audits your workloads for unintended access paths.

Preventing Future AccessDenied Errors

Prevention Strategy How to Implement Benefit
1. Use Infrastructure as Code Define roles with CloudFormation or Terraform Version-controlled, repeatable trust policies
2. Implement Policy Validator in CI/CD Use tools like IAM Policy Simulator Catch policy errors before deployment
3. Centralize Role Management Use AWS Organizations SCPs Enforce organizational standards for trust policies
4. Regular Access Reviews Use IAM Access Analyzer Identify external access and policy issues
5. Monitor with CloudTrail Alerts Set up EventBridge rules for AccessDenied Get notified immediately when access fails

Frequently Asked Questions

1. How do I find the correct ARN for my IAM user or role?

Run aws sts get-caller-identity and look at the Arn field in the JSON response. This shows your current identity ARN, whether you're an IAM user or an assumed role.

2. Can I assume a role in the same account without a trust policy?

No. Even for same-account role assumption, the trust policy must explicitly allow your identity to assume the role. However, you can use the PassRole permission to grant an IAM user the ability to pass a role to AWS services without assuming it directly.

3. What's the difference between sts:AssumeRole and iam:PassRole?

sts:AssumeRole is the action performed by the identity that wants to become the role (switching identities). iam:PassRole is a permission granted to an IAM user that allows them to assign a role to an AWS service (like EC2 or Lambda) so the service can assume that role on their behalf.

4. How do I troubleshoot if the MFA device is lost or broken?

If you can't provide the MFA code but need to assume the role immediately:

  1. Temporarily remove the MFA condition from the trust policy (not recommended for long-term use)
  2. Use an alternative MFA device if you have multiple configured
  3. Contact your AWS administrator to update the MFA device in IAM

5. Can I assume a role from a different AWS partition (e.g., from aws to aws-cn)?

No. Trust policies cannot span AWS partitions (commercial, China, GovCloud). You cannot assume a role in the AWS GovCloud partition from an IAM user in the standard AWS partition.

6. How long are temporary credentials from AssumeRole valid?

By default, 3600 seconds (1 hour). You can request a shorter duration using the --duration-seconds parameter (minimum 900 seconds, maximum 43200 seconds depending on the role's maximum session duration setting).

7. What is the ExternalId and why is it important?

The ExternalId is a unique identifier that helps prevent the "confused deputy" problem. When a third party needs to assume a role in your account, they include this ID in their AssumeRole request. It ensures that the role can only be assumed by the specific third party, even if other identities somehow get access to the role's ARN.

8. How do I find what permissions an assumed role has?

Once you've assumed a role, run aws iam list-attached-role-policies --role-name ROLE_NAME or aws iam list-role-policies --role-name ROLE_NAME to see its permission policies. You can also use the IAM Policy Simulator to test what actions the role can perform.

9. Can I assume a role without MFA if the trust policy requires it?

No. If the trust policy has "Bool": {"aws:MultiFactorAuthPresent": "true"}, the AssumeRole call will fail unless you provide a valid MFA token. This is a security requirement that cannot be bypassed without modifying the trust policy.

10. How do I troubleshoot "AccessDenied" when using the AWS Management Console to switch roles?

The process is similar, but the error might be less specific. Check:

  • The role's trust policy allows your IAM user or account
  • Your IAM user has sts:AssumeRole permission
  • You're using the correct account ID in the role ARN
  • The role isn't deactivated

11. What should I do if the role trust policy is too permissive?

If the trust policy uses "Principal": {"AWS": "*"} or allows entire accounts when it should only allow specific users, update it to use specific ARNs. Then, audit your IAM users and roles to ensure they don't have unintended sts:AssumeRole permissions.

12. How do I set up role assumption for cross-account access with Organizations?

When using AWS Organizations, you typically:

  1. Create a role in the target account with a trust policy that allows the organization's master account or specific member accounts
  2. Grant IAM users in the master account permission to assume that role
  3. Use the sts:AssumeRole action with the role ARN from the master account

13. Can I assume a role from an EC2 instance?

Yes, if the EC2 instance has an IAM instance profile attached. The instance profile contains a role that the instance can assume. Applications on the instance can then use the instance metadata service to retrieve temporary credentials for that role.

14. How do I rotate the external ID for a role?

Update the trust policy's ExternalId condition and notify the third party that needs to assume the role. They must update their tools with the new ExternalId before their next AssumeRole call.

15. What's the difference between a trust policy and a permission policy?

A trust policy is attached to a role and defines who can assume the role (which identities are trusted). A permission policy is attached to a role (or user) and defines what actions the assumed identity can perform and on which resources.

16. How do I check if my AWS CLI is using the correct profile?

Run aws configure list to see which credentials and region are being used. You can also set the AWS_PROFILE environment variable to explicitly specify which profile to use: export AWS_PROFILE=profile-assume-role.

Conclusion: Building a Secure Trust Relationship

AccessDenied errors on sts:AssumeRole are almost always one of two issues: either the trust policy doesn't allow your identity to assume the role, or your identity lacks permission to perform the AssumeRole action. By following the diagnostic flowchart in this guide, you can systematically identify and fix the problem.

Remember that IAM role trust relationships are the foundation of secure cross-account access in AWS. By following the principle of least privilege—using specific principals rather than account-wide access, requiring MFA for human access, and using ExternalIds for programmatic access—you can maintain security while enabling the access your applications and teams need.

Revision note. Written Sep 2026. AWS occasionally updates IAM policy evaluation logic, so always check the official AWS IAM documentation for the latest changes. If you've been fighting this AccessDenied error, I hope this guide saved you the hours it took me to piece together all these failure points—hang in there, AWS security is a deep rabbit hole, but you've got this.

Related