Masterpoint stands with Ukraine. Here’s how you can help Ukraine with just a few clicks. >

Fix IAM Trust Policy Errors using AWS IAM Identity Center SSO Role Regional ARN

By Yangci Ou
If your IAM trust policy isn't working with AWS IAM Identity Center SSO roles, it might be because us-east-1 does not have the region in the ARN while other regions do.
Fix IAM Trust Policy Errors using AWS IAM Identity Center SSO Role Regional ARN

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!

Get a standardized, predictable, and efficient infrastructure management process

Skip the stress and let us organize the mess. Reach out today for a free assessment.

Schedule Your Free Assessment →