What "AccessDenied when calling the AssumeRole operation" means, and how to fix it
Updated · 4 min read
The full message looks like this:
An error occurred (AccessDenied) when calling the AssumeRole operation: User: arn:aws:iam::111122223333:user/alice is not authorized to perform: sts:AssumeRole on resource: arn:aws:iam::444455556666:role/deployUsing a role takes two yeses. The role's trust policy has to let you in, and any conditions in it have to be met. And, nearly always, your own permissions have to allow sts:AssumeRole on that role too. (The exception is a role in your own account whose trust policy names your IAM user exactly.) The error doesn't say which one said no, so check both, in this order.
1. Check who you are
aws sts get-caller-identityCompare the Arn it prints with the User: in the error. The problem is often right here: the wrong --profile, or leftover AWS_ACCESS_KEY_ID environment variables, which take priority over AWS_PROFILE. If you signed in through IAM Identity Center (SSO), you're an assumed-role/AWSReservedSSO_… session, not an IAM user.
2. Read the role's trust policy
In the role's account, open IAM → Roles → the role → Trust relationships, or:
aws iam get-role --role-name deploy --query Role.AssumeRolePolicyDocumentPaste it into the IAM policy explainer to read it in plain English. Then check:
- The
Principalnames you. Either your account (arn:aws:iam::111122223333:root, which means any user or role there whose own permissions allow it) or your exact user or role ARN. - It isn't holding a stale ID. If the user or role it names was deleted and then created again with the same name, the trust policy now holds the old one's internal ID (something like
AROA…orAIDA…) in place of the ARN, and the new one doesn't match. Edit the policy and put the ARN back. - You meet its conditions. The common ones:
sts:ExternalId: pass it with--external-id. Third-party tools that connect to your account usually require one.aws:MultiFactorAuthPresent: you need an MFA code, with--serial-number arn:aws:iam::111122223333:mfa/alice --token-code 123456.- Source identity and session tags: setting a source identity (
--source-identity) also needssts:SetSourceIdentityallowed in the trust policy, and passing tags needssts:TagSession. aws:PrincipalOrgID: your account has to be in that AWS Organization.
3. Check your own permissions
Unless the trust policy names your IAM user exactly and the role is in your own account, your user or role also needs a policy like this. It's always needed when the role is in another account, or when the trust policy trusts your whole account (:root).
{
"Effect": "Allow",
"Action": "sts:AssumeRole",
"Resource": "arn:aws:iam::444455556666:role/deploy"
}You can check your side without guessing:
aws iam simulate-principal-policy \
--policy-source-arn arn:aws:iam::111122223333:user/alice \
--action-names sts:AssumeRole \
--resource-arns arn:aws:iam::444455556666:role/deployAn EvalDecision of allowed means your own policies are fine. implicitDeny means nothing allows it, and explicitDeny means something forbids it. The simulator looks only at your side, not the role's trust policy.
Two things can still say no when both policies look right: a service control policy from your AWS Organization, and a permissions boundary on your user or role. Newer error messages sometimes end with the reason, such as "because no identity-based policy allows the sts:AssumeRole action", which tells you which side to look at.
4. Try it directly
aws sts assume-role \
--role-arn arn:aws:iam::444455556666:role/deploy \
--role-session-name test \
--external-id your-external-idLeave out --external-id if the trust policy doesn't ask for one.
Errors that look similar but aren't
ValidationErroraboutDurationSeconds: you asked for a longer session than the role allows. Sessions taken from another role's session ("role chaining") are capped at one hour.ExpiredTokenorInvalidClientTokenId: your own credentials have expired or are wrong. Nothing to do with the role.RegionDisabledException: STS is turned off for that region in the account. Use another region's endpoint, or turn it on in IAM's account settings.
Or in Cloud GUI
Cloud GUI connects to your account with exactly this mechanism. You create a role from its template, and the role trusts Cloud GUI only with your connection's external ID, and only for sessions that carry the email of the person using it as a source identity, so every session is in your CloudTrail. Delete the role's stack and Cloud GUI's next AssumeRole gets AccessDenied, by design; Cloud GUI then tells you it can't reach the account any more.
One thing we learned building it: putting sts:SetSourceIdentity in the same statement as an sts:ExternalId condition made it fail, so our trust policy allows it in a separate statement. You can read the whole template, or try it in the policy explainer (it's one of the samples).