The Scenario
You’re running a CI/CD pipeline on Friday afternoon, trying to deploy a new microservice to a staging environment. The pipeline uses aws sts assume-role to switch from a service account in the “tools” AWS account to a deployment role in the “staging” account. The build fails at the role assumption step with an “AccessDenied” error.
Symptoms
aws sts assume-role --role-arn "arn:aws:iam::123456789012:role/deploy-role" --role-session-name "ci-session"returnsAn error occurred (AccessDenied) when calling the AssumeRole operation: User: arn:aws:iam::987654321098:user/ci-user is not authorized to perform: sts:AssumeRole on resource: arn:aws:iam::123456789012:role/deploy-role- CloudTrail logs show
"errorMessage": "AccessDenied"for theAssumeRoleAPI call. - The error persists even with correct role ARN and region settings.
Root Cause
This error occurs when the IAM identity (user or role) making the AssumeRole call does not have the necessary permissions to assume the target role. Two distinct permission sets must be in place: (1) the source identity must have an IAM policy allowing sts:AssumeRole on the target role ARN, and (2) the target role must have a trust policy that allows the source identity to assume it. The error is most commonly caused by a missing or misconfigured trust policy on the target role.
Resolution (Step-by-Step)
-
Verify the source identity has
sts:AssumeRolepermission
Check the IAM policy attached to the user/role making the call. It must include an action like:{ "Effect": "Allow", "Action": "sts:AssumeRole", "Resource": "arn:aws:iam::123456789012:role/deploy-role" }Use the AWS CLI to check the effective permissions:
aws iam simulate-principal-policy \ --policy-source-arn arn:aws:iam::987654321098:user/ci-user \ --action-names sts:AssumeRole \ --resource-arns arn:aws:iam::123456789012:role/deploy-role -
Inspect the target role’s trust policy
Get the trust policy of the role you’re trying to assume:aws iam get-role --role-name deploy-role --query 'Role.AssumeRolePolicyDocument'Ensure it includes the source identity. For a cross-account scenario, it should look like:
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "AWS": "arn:aws:iam::987654321098:user/ci-user" }, "Action": "sts:AssumeRole" } ] }For a service role (e.g., EC2, Lambda), the principal should be the service, not an ARN.
-
Check for explicit denies or condition keys
Look forConditionblocks in either policy that might block the call. Common conditions include:aws:SourceIp— if the request comes from outside allowed IPsaws:MultiFactorAuthPresent— if MFA is required but not usedaws:RequestedRegion— if the region is restricted Test by temporarily removing conditions to isolate the issue.
-
Validate ARN syntax and account IDs
Double-check the role ARN for typos. A common mistake is usingarn:aws:iam::123456789012:role/deploy-rolewhen the actual role name isdeploy-role-v2or the account ID is wrong.
Why This Sometimes Doesn’t Work
The standard fix of adding the trust policy fails when the source identity is an AWS service (like EC2 or Lambda) using a service-linked role. In that case, the trust policy must reference the service principal (e.g., ec2.amazonaws.com) and include a condition like aws:SourceArn to restrict which resources can assume the role. Without that condition, any instance in the account can assume the role, which is often blocked by security teams. Also, service control policies (SCPs) in AWS Organizations can override IAM permissions silently — check the account’s effective SCPs if the above steps don’t work.
Verification
Run the assume-role command again and check for a successful response containing Credentials:
aws sts assume-role --role-arn "arn:aws:iam::123456789012:role/deploy-role" --role-session-name "test-session"
Expected output includes:
{
"Credentials": {
"AccessKeyId": "ASIA...",
"SecretAccessKey": "...",
"SessionToken": "...",
"Expiration": "2026-07-28T..."
}
}
Common Follow-up Questions
Q: Why does AssumeRole work from the AWS console but not from the CLI?
A: The console uses the same IAM permissions, but it might be using a different role or session context. Check if the CLI is using the correct profile or environment variables (e.g., AWS_PROFILE, AWS_ACCESS_KEY_ID).
Q: Can I assume a role in the same account without a trust policy?
A: No. Even within the same account, the target role must have a trust policy that allows the source identity to assume it. The source identity must also have sts:AssumeRole permission on that role.
Q: How do I debug “AccessDenied” when using an EC2 instance profile?
A: The instance profile’s role must have sts:AssumeRole permission, and the target role must trust the instance role’s ARN (not the service principal). Use aws sts get-caller-identity from the instance to confirm the role being used.
Prevent This in the Future
This class of IAM permission error often goes unnoticed until a deployment or scaling event fails. Proactive monitoring of AssumeRole failures in CloudTrail can alert your team before a broken pipeline blocks releases. Teams using Better Stack can set up real-time alerts on CloudTrail errors and correlate them with deployment events, reducing mean time to resolution.
Set up uptime monitoring and alerts with Better Stack