Skip to content

Cross-Account IAM Roles

A cross-account IAM role grants access to resources in one AWS account — the trusting account — to principals in another — the trusted account. It is the standard building block for least privilege in a multi-account estate, and the canonical reference for the mechanics of role assumption.

For the choice between this and a resource-based policy, see Cross-account access: resource-based policies vs IAM roles.

graph TB subgraph Trusted["Trusted account — where the caller lives"] U[User or role] AP[Policy allowing sts:AssumeRole] U -->|has| AP end subgraph Trusting["Trusting account — where the resources live"] R[IAM role] TP[Trust policy] PP[Permission policy] AWSRes[AWS resources] R -->|has| TP R -->|has| PP PP -->|grants access to| AWSRes end U -->|1. Calls AssumeRole| STS[AWS STS] AP -->|2. Permits the call| STS TP -->|3. Accepts the caller| STS STS -->|4. Returns temporary credentials| U U -->|5. Calls the resources with those credentials| AWSRes

Three separate policies have to line up. Missing any one of them produces an AccessDenied that looks the same from the outside.

1. Trust policy, on the role in the trusting account

Section titled “1. Trust policy, on the role in the trusting account”

States who may assume the role:

{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::111122223333:role/DeploymentRole"
},
"Action": "sts:AssumeRole",
"Condition": {
"StringEquals": {
"aws:PrincipalOrgID": "o-example123"
}
}
}
]
}

Naming the specific role, rather than arn:aws:iam::111122223333:root, keeps the grant with one identity instead of delegating it to the other account’s administrators.

States what a session holding the role may do:

{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:GetObject",
"s3:ListBucket"
],
"Resource": [
"arn:aws:s3:::example-bucket",
"arn:aws:s3:::example-bucket/*"
]
}
]
}

3. AssumeRole permission, in the trusted account

Section titled “3. AssumeRole permission, in the trusted account”

Attached to the user or role that will do the assuming:

{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "sts:AssumeRole",
"Resource": "arn:aws:iam::444455556666:role/CrossAccountRole"
}
]
}
  1. Setup. The trusting account creates the role with its trust and permission policies. The trusted account attaches sts:AssumeRole permission to the identities that need it. No role has to be created in the trusted account.
  2. Assumption. The caller invokes sts:AssumeRole. AWS STS checks both the trust relationship and the caller’s own permission to make the call. If both hold, it returns temporary credentials.
  3. Access. The credentials carry the role’s permissions and expire — between 15 minutes and the role’s MaxSessionDuration, which can be up to 12 hours. A session obtained by chaining from an already-assumed role is capped at one hour.
  • Central security tooling. A security account holds the tools and assumes a read-only role in every other account for logging, monitoring and compliance.
  • Deployment. A pipeline in a build account assumes a narrowly scoped role in each target account, so no account holds a long-lived deployment credential.
  • Human access across environments. Engineers work in development, staging and production through roles assigned by IAM Identity Center rather than through per-account users.
  • Third parties. A vendor’s account assumes a role in yours, with an external ID and a scope limited to what the integration actually needs.

External IDs. When granting access to a third party, require an sts:ExternalId condition in the trust policy. Without it, a vendor that holds roles in many customer accounts can be tricked into using its access to yours on someone else’s behalf — the confused deputy problem. The external ID should be a value you choose and give to the vendor, not one they choose.

Bound the maximum. Use permissions boundaries on the roles, and service control policies at the OU level, so that a mistake in a role’s permission policy has a ceiling.

Monitor. CloudTrail records every assumption with the session name and the source. Alarm on assumptions from unexpected principals or at unexpected times, and review the roles’ permissions against what the sessions actually do.

Terminal window
# In the trusting account: create the role
aws iam create-role \
--role-name CrossAccountRole \
--assume-role-policy-document file://trust-policy.json
# In the trusting account: attach permissions to the role
aws iam attach-role-policy \
--role-name CrossAccountRole \
--policy-arn arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess
# In the trusted account: allow a principal to assume it
aws iam put-role-policy \
--role-name DeploymentRole \
--policy-name CrossAccountAssume \
--policy-document file://assume-role-policy.json
# From the trusted account: assume the role
aws sts assume-role \
--role-arn arn:aws:iam::444455556666:role/CrossAccountRole \
--role-session-name deployment-session

Access denied when calling AssumeRole

  • Confirm the trust policy names the calling principal, and that the ARN matches exactly
  • Confirm the caller has sts:AssumeRole on the specific role ARN
  • Check for an external ID that is required but not supplied, or supplied but not required
  • Check whether a service control policy above either account denies the action
  • If the role in the trust policy was deleted and recreated, the stored principal ID no longer matches — re-save the trust policy with the current ARN

Access denied after assuming the role

  • Confirm the role’s permission policy covers the action and the exact resource ARN
  • Check for a permissions boundary or session policy narrowing the session
  • Check for a resource-based policy on the target that denies, or does not allow, the role
  • For a chained session, remember the one-hour cap — an expired session and a missing permission look identical

Unexpected access levels

  • Use IAM Access Analyzer to see what the role can actually reach
  • Check inherited permissions from group membership on the calling identity
  • Evaluate resource-based policies on the targets, which can grant access independently of the role