Introduction
An IAM role can have a perfectly valid trust policy and still reject sts:AssumeRole. The reason is that AWS evaluation does not stop at the trust relationship. Any explicit deny in the caller path, permission boundary, session policy, or organization SCP overrides the allow. That is why these incidents are frustrating: the role looks right, but the request is denied anyway.
Symptoms
AssumeRolereturnsAccessDenied- The trust policy appears correct when reviewed in isolation
- One principal can assume the role while another cannot
- The failure started after an SCP, permissions boundary, or tag condition change
Common Causes
- The caller has an explicit deny on
sts:AssumeRole - A permissions boundary blocks role assumption even though the attached policy allows it
- An organization SCP denies the action for that account or OU
- The trust policy requires conditions such as
ExternalId, MFA, or session tags that the caller is not providing
Step-by-Step Fix
- 1.Verify the trust policy is not the only thing under review
- 2.Start by acknowledging that the trust policy is necessary but not sufficient for a successful assume-role path.
- 3.Inspect the caller-side policies and boundaries for explicit deny
- 4.Search for
Effect: Denyonsts:AssumeRoleor broader statements that cover the target role path.
aws iam list-attached-user-policies --user-name deploy-user
aws iam list-attached-role-policies --role-name ci-runner-role- 1.Check organization SCPs and required trust policy conditions
- 2.If the role is cross-account or guarded by
ExternalId, MFA, or tag conditions, verify the caller request actually satisfies them.
{
"Effect": "Allow",
"Principal": { "AWS": "arn:aws:iam::123456789012:root" },
"Action": "sts:AssumeRole",
"Condition": {
"StringEquals": {
"sts:ExternalId": "deploy-prod"
}
}
}- 1.Retest with the exact caller context after removing the conflicting deny
- 2.Use the same profile, principal, and tags that failed originally so you know the fix applies to the real path rather than a different admin identity.
Prevention
- Review AssumeRole failures across trust policy, caller policy, permissions boundary, and SCP together
- Document required conditions such as
ExternalId, MFA, and session tags for every cross-account role - Avoid broad deny statements that unintentionally catch
sts:AssumeRole - Test role assumption paths after organization policy changes, not just after trust policy edits