Fix IAM Trust Policy Errors using AWS IAM Identity Center SSO Role Regional ARN
By Yangci Ou
When working with AWS IAM Identity Center (formerly also known as AWS Single-Sign-on SSO), one of the most frustrating errors you can face is configuring trust policies that properly reference SSO role ARNs created by IAM Identity Center.
You set up a trust policy that looks correct, references the correct SSO role ARN, but still get an AccessDenied
error.
It might be because of this critical gotcha edge case: region-specific paths in the SSO role ARNs.
I faced this issue myself, along with others on StackOverflow, AWS re:Post, and even Reddit. When pairing with my colleague, Veronika, we asked ChatGPT to troubleshoot this, but even it was unable to provide the correct solution. It’s such a small but important piece of information that’s buried in the AWS documentation, so I’ll cover it in this article to hopefully help others avoid scratching their heads for an hour like I did, and make it easily findable on the Internet.
The Problem
You want to allow an AWS SSO role generated by a permission set to assume another IAM role.
An user from the AWS IAM Identity Center assumed into the AWS SSO role, and should be able to assume another IAM role, but they keep hitting this error:
An error occurred (AccessDenied) when calling the AssumeRole operation: User: arn:aws:sts::111111111111:assumed-role/AWSReservedSSO_AWSAdministratorAccess_abc123def456/user@company.com is not authorized to perform: sts:AssumeRole on resource: arn:aws:iam::111111111111:role/target-role
Your trust policy looks correct, it allows an user from the 111111111111
account and where their current role is AWSReservedSSO_AWSAdministratorAccess
to AssumeRole
into the target role that this trust policy is attached to:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "sts:AssumeRole",
"Condition": {
"ArnLike": {
"aws:PrincipalArn": [
"arn:aws:iam::111111111111:role/aws-reserved/sso.amazonaws.com/AWSReservedSSO_AWSAdministratorAccess*"
]
}
}
}
]
}
But it still doesn’t work. Why?
The Regional Path Edge Case
This issue stems from a critical edge case: “when your identity source in IAM Identity Center is hosted in us-east-1
, there is no aws-region
in the ARN.”
However, when the IAM Identity Center is provisioned outside the AWS us-east-1
region, the service WILL include the region in the IAM role ARN.
Here’s the official AWS documentation that states this: Referencing permission sets in resource policies.
Expected role ARN pattern:
arn:aws:iam::123456789012:role/aws-reserved/sso.amazonaws.com/AWSReservedSSO_RoleName_abc123
Actual role ARN in non-us-east-1 regions:
arn:aws:iam::123456789012:role/aws-reserved/sso.amazonaws.com/AWS-REGION/AWSReservedSSO_RoleName_abc123
e.g. us-west-2:
arn:aws:iam::123456789012:role/aws-reserved/sso.amazonaws.com/us-west-2/AWSReservedSSO_RoleName_abc123
Notice the /us-west-2/
component that gets injected into the path.
Diagnosing the Issue and Confirming the ARN
Let’s run a series of commands to diagnose the issue and confirm the ARN. Once you’re assumed into the SSO role:
Step 1. Get Your Current Identity Information
aws sts get-caller-identity
This will return something like:
{
"UserId": "AROA3L3FW5XXXXXXXXXXX:yangci.ou@masterpoint.io",
"Account": "111111111111",
"Arn": "arn:aws:sts::111111111111:assumed-role/AWSReservedSSO_AWSAdministratorAccess_9999999999999999/yangci.ou@masterpoint.io"
}
Step 2. Check the SSO role ARN
Extract just the role name from the ARN: AWSReservedSSO_AWSAdministratorAccess_9999999999999999
(the suffix will be different for everyone) and now you retrieve information about the role:
aws iam get-role --role-name AWSReservedSSO_AWSAdministratorAccess_9999999999999999
This reveals the critical information:
{
"Role": {
"Path": "/aws-reserved/sso.amazonaws.com/",
"RoleName": "AWSReservedSSO_AdministratorAccess_9999999999999999",
"RoleId": "AROA3L3FW5XXXXXXXXXXX",
"Arn": "arn:aws:iam::111111111111:role/aws-reserved/sso.amazonaws.com/us-west-2/AWSReservedSSO_AdministratorAccess_9999999999999999", // Note the /us-west-2/ path here!
...
}
}
Notice the /us-west-2/
in the path! When AWS IAM Identity Center & SSO is configured outside of us-east-1
, the role path includes the region. This means your condition pattern needs to account for this regional component.
Your assumed role ARN: arn:aws:sts::111111111111:assumed-role/AWSReservedSSO_AWSAdministratorAccess_9999999999999999/user
Actual IAM role ARN: arn:aws:iam::111111111111:role/aws-reserved/sso.amazonaws.com/us-west-2/AWSReservedSSO_AWSAdministratorAccess_9999999999999999
Your trust policy should include the region in the path, like the IAM role ARN above. It’s worth noting that if Disaster Recovery (multi-region) is a priority or when using IaC such as Terraform, instead of hardcoding the region like us-west-2, it may be better to use /*/
(which doesn’t impose any security risks either), e.g.:
arn:aws:iam::*:role/aws-reserved/sso.amazonaws.com/*/AWSReservedSSO_AWSAdministratorAccess*
Why This Gotcha / Edge Case Exists
Unlike the AWS IAM service that handles IAM users, roles, policies, etc. which is global and region agnostic, IAM Identity Center is region-specific. AWS administrators has to provision the IAM Identity Center in a specified region, hence the regional path in the ARN.
As for why us-east-1
doesn’t have the regional path, it’s likely because AWS considers it the default region and doesn’t need to be specified and it’s some legacy behavior when they first implemented it. There are other patterns like this, like even though AWS CloudFront is a “global” resource, it’s still hosted in the us-east-1
region. But, there’s no documentation that explicitly states why this is, so this is an assumption.
Example IAM Trust Policy
Here’s an example IAM trust policy that allows SSO users from the Administrator permission set to assume the target role, if the IAM Identity Center is provisioned outside of us-east-1
:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::123456789012:root" // We can use this to allow multiple accounts.
},
"Action": ["sts:AssumeRole", "sts:TagSession"],
"Condition": {
"ArnLike": {
"aws:PrincipalArn": [
// It's worth noting that if Disaster Recovery (multi-region) is a priority
// or when using IaC such as Terraform, instead of hardcoding the region like us-west-2,
// it may be better to use `/*/` (which doesn't impose any security risks either).
// The wildcard (*) at the end of the ARN AWSReservedSSO_AWSAdministratorAccess* is
// also important, as there's no need to hardcode the randomly generated ID.
"arn:aws:iam::*:role/aws-reserved/sso.amazonaws.com/us-west-2/AWSReservedSSO_AWSAdministratorAccess*"
]
}
}
}
]
}
If you’re using Terraform or OpenTofu, here’s a data
source example:
data "aws_iam_policy_document" "assume_role_policy" {
statement {
effect = "Allow"
principals {
type = "AWS"
identifiers = ["arn:aws:iam::123456789012:root"] # We can use this to allow multiple accounts.
}
actions = [
"sts:AssumeRole",
"sts:TagSession"
]
condition {
test = "ArnLike"
variable = "aws:PrincipalArn"
values = [
# It's worth noting that if Disaster Recovery (multi-region) is a priority
# or when using IaC such as Terraform, instead of hardcoding the region like us-west-2,
# it may be better to use `/*/` (which doesn't impose any security risks either).
# The wildcard (*) at the end of the ARN AWSReservedSSO_AWSAdministratorAccess* is also important,
# as there's no need to hardcode the randomly generated ID.
"arn:aws:iam::*:role/aws-reserved/sso.amazonaws.com/us-west-2/AWSReservedSSO_AWSAdministratorAccess*"
]
}
}
}
See more examples in the AWS documentation.
👋 If you're ready to take your infrastructure to the next level, we're here to help. We love to work together with engineering teams to help them build well-documented, scalable, automated IaC that make their jobs easier. Get in touch!