Overview

When working with AWS IAM, you may need to configure one IAM Role to assume another IAM Role. This is a common pattern for implementing the principle of least privilege and enabling cross-account access.

Important: The configuration method differs depending on whether the two IAM Roles are in the same AWS account or in different AWS accounts.

flowchart TB
    subgraph Overview[IAM Role to Role AssumeRole Overview]
        direction TB
        subgraph SameAccount[Same Account Scenario]
            direction LR
            RoleA1[Role A<br/>Source Role] -->|1. Trust Relationship<br/>on Role B| RoleB1[Role B<br/>Target Role]
            RoleA1 -.->|2. Auto Permission<br/>No Policy Needed| RoleB1
        end
        
        subgraph CrossAccount[Cross-Account Scenario]
            direction LR
            Account1[Account 111111111111<br/>Source Account]
            Account2[Account 222222222222<br/>Target Account]
            
            RoleA2[Role A<br/>Source Role] -->|1. Trust Relationship<br/>on Role B| RoleB2[Role B<br/>Target Role]
            RoleA2 -->|2. sts:AssumeRole Policy<br/>Required on Role A| RoleB2
            
            Account1 --> RoleA2
            Account2 --> RoleB2
        end
    end
    
    style Overview fill:#fff,stroke:#000,stroke-width:2px
    style SameAccount fill:#fff,stroke:#000,stroke-width:1px
    style CrossAccount fill:#fff,stroke:#000,stroke-width:1px
    style RoleA1 fill:#fff,stroke:#000,stroke-width:1px
    style RoleB1 fill:#fff,stroke:#000,stroke-width:1px
    style RoleA2 fill:#fff,stroke:#000,stroke-width:1px
    style RoleB2 fill:#fff,stroke:#000,stroke-width:1px
    style Account1 fill:#fff,stroke:#000,stroke-width:1px
    style Account2 fill:#fff,stroke:#000,stroke-width:1px

Same Account: Role to Role AssumeRole

When both IAM Roles exist in the same AWS account, the configuration is straightforward.

Scenario

Let’s assume we have two IAM Roles:

  • Role A: The role that will perform the AssumeRole action
  • Role B: The role that will be assumed

Both roles are in the same AWS account (e.g., account 111111111111).

flowchart LR
    subgraph SameAccountArch[Same Account Architecture]
        direction LR
        subgraph Account[Account 111111111111]
            direction TB
            RoleA[Role A<br/>Source Role<br/>Performs AssumeRole]
            RoleB[Role B<br/>Target Role<br/>Gets Assumed]
            
            RoleA -->|sts:AssumeRole| RoleB
        end
        
        TrustPolicy[Trust Relationship<br/>on Role B<br/>Principal: Role A]
        RoleB -.->|Configure| TrustPolicy
    end
    
    style SameAccountArch fill:#fff,stroke:#000,stroke-width:2px
    style Account fill:#fff,stroke:#000,stroke-width:1px
    style RoleA fill:#fff,stroke:#000,stroke-width:1px
    style RoleB fill:#fff,stroke:#000,stroke-width:1px
    style TrustPolicy fill:#fff,stroke:#000,stroke-width:1px

Configuration

To allow Role A to assume Role B, you only need to modify the Trust Relationship (Trust Policy) of Role B.

Step 1: Update Role B’s Trust Relationship

Edit Role B’s Trust Relationship policy to include Role A as a trusted principal:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::111111111111:role/Role_A"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}

Key Points:

  • The Principal element specifies which IAM Role is allowed to assume this role
  • Use the full ARN of Role A: arn:aws:iam::ACCOUNT_ID:role/ROLE_NAME
  • The Action must be sts:AssumeRole

Step 2: Verify Role A Has AssumeRole Permission

In the same account scenario, Role A automatically has permission to call sts:AssumeRole on Role B once the Trust Relationship is configured. No additional policy is required on Role A.

Understanding the Principal Element

The Principal element in a Trust Relationship policy is used to specify which entities (IAM Users, IAM Roles, AWS Services, or other AWS resources) are allowed to assume the role.

For more detailed information about the Principal element, refer to the AWS IAM documentation.

Cross-Account: Role to Role AssumeRole

When the two IAM Roles are in different AWS accounts, the configuration requires an additional step.

Scenario

Let’s assume:

  • Role A: Exists in AWS account 111111111111 (source account)
  • Role B: Exists in AWS account 222222222222 (target account)

Role A needs to assume Role B across accounts.

flowchart TB
    subgraph CrossAccountArch[Cross-Account Architecture]
        direction TB
        subgraph SourceAccount[Account 111111111111<br/>Source Account]
            direction TB
            RoleA[Role A<br/>Source Role]
            PolicyA[Permissions Policy<br/>sts:AssumeRole<br/>Resource: Role B ARN]
            RoleA -.->|Requires| PolicyA
        end
        
        subgraph TargetAccount[Account 222222222222<br/>Target Account]
            direction TB
            RoleB[Role B<br/>Target Role]
            TrustPolicy[Trust Relationship<br/>Principal: Role A ARN<br/>Action: sts:AssumeRole]
            RoleB -.->|Requires| TrustPolicy
        end
        
        RoleA -->|1. Check Policy| PolicyA
        PolicyA -->|2. AssumeRole Request| RoleB
        RoleB -->|3. Check Trust Relationship| TrustPolicy
        TrustPolicy -->|4. Grant Access| RoleB
    end
    
    style CrossAccountArch fill:#fff,stroke:#000,stroke-width:2px
    style SourceAccount fill:#fff,stroke:#000,stroke-width:1px
    style TargetAccount fill:#fff,stroke:#000,stroke-width:1px
    style RoleA fill:#fff,stroke:#000,stroke-width:1px
    style RoleB fill:#fff,stroke:#000,stroke-width:1px
    style PolicyA fill:#fff,stroke:#000,stroke-width:1px
    style TrustPolicy fill:#fff,stroke:#000,stroke-width:1px

Configuration

For cross-account AssumeRole, you need to configure both roles:

Step 1: Update Role B’s Trust Relationship (Target Account)

Edit Role B’s Trust Relationship policy in account 222222222222:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::111111111111:role/Role_A"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}

Note: The Trust Relationship configuration is identical to the same-account scenario. The difference is that the ARN points to a role in a different account.

Step 2: Add AssumeRole Policy to Role A (Source Account)

This is the critical difference! Role A must have an IAM policy that explicitly allows it to assume Role B.

Add the following policy to Role A in account 111111111111:

{
  "Version": "2012-10-17",
  "Statement": {
    "Effect": "Allow",
    "Action": "sts:AssumeRole",
    "Resource": "arn:aws:iam::222222222222:role/Role_B"
  }
}

Key Points:

  • The Action is sts:AssumeRole
  • The Resource must be the full ARN of Role B in the target account
  • This policy grants Role A permission to perform the AssumeRole operation

Why the Additional Policy is Required

In cross-account scenarios, AWS requires explicit permission on the source role (Role A) to assume roles in other accounts. This is a security measure to prevent unauthorized cross-account access.

Complete Example: Cross-Account Setup

Here’s a complete example showing both configurations:

flowchart TB
    subgraph CompleteExample[Complete Cross-Account Example]
        direction TB
        subgraph SourceAcc[Account 111111111111 - Source]
            direction TB
            EC2Service[EC2 Service<br/>Can Assume Role A]
            RoleA[Role A]
            RoleATrust[Role A Trust Relationship<br/>Principal: ec2.amazonaws.com]
            RoleAPolicy[Role A Permissions Policy<br/>Action: sts:AssumeRole<br/>Resource: Role B ARN]
            
            EC2Service -->|Assumes| RoleA
            RoleA -.->|Has| RoleATrust
            RoleA -.->|Has| RoleAPolicy
        end
        
        subgraph TargetAcc[Account 222222222222 - Target]
            direction TB
            RoleB[Role B]
            RoleBTrust[Role B Trust Relationship<br/>Principal: Role A ARN<br/>Action: sts:AssumeRole]
            RoleBPolicy[Role B Permissions Policy<br/>Action: s3:GetObject<br/>Resource: s3://example-bucket/*]
            
            RoleB -.->|Has| RoleBTrust
            RoleB -.->|Has| RoleBPolicy
        end
        
        RoleA -->|1. AssumeRole Request| RoleB
        RoleB -->|2. Access S3 Resources| S3Bucket[S3 Bucket<br/>example-bucket]
    end
    
    style CompleteExample fill:#fff,stroke:#000,stroke-width:2px
    style SourceAcc fill:#fff,stroke:#000,stroke-width:1px
    style TargetAcc fill:#fff,stroke:#000,stroke-width:1px
    style EC2Service fill:#fff,stroke:#000,stroke-width:1px
    style RoleA fill:#fff,stroke:#000,stroke-width:1px
    style RoleB fill:#fff,stroke:#000,stroke-width:1px
    style RoleATrust fill:#fff,stroke:#000,stroke-width:1px
    style RoleAPolicy fill:#fff,stroke:#000,stroke-width:1px
    style RoleBTrust fill:#fff,stroke:#000,stroke-width:1px
    style RoleBPolicy fill:#fff,stroke:#000,stroke-width:1px
    style S3Bucket fill:#fff,stroke:#000,stroke-width:1px

Account 111111111111 (Source Account)

Role A - Trust Relationship:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Service": "ec2.amazonaws.com"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}

Role A - Permissions Policy:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": "sts:AssumeRole",
      "Resource": "arn:aws:iam::222222222222:role/Role_B"
    }
  ]
}

Account 222222222222 (Target Account)

Role B - Trust Relationship:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::111111111111:role/Role_A"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}

Role B - Permissions Policy:

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

Best Practices

  1. Use External IDs for Cross-Account: Add an ExternalId condition to the Trust Relationship for additional security:

    {
      "Condition": {
        "StringEquals": {
          "sts:ExternalId": "unique-external-id-here"
        }
      }
    }
    
  2. Principle of Least Privilege: Grant only the minimum permissions required in the assumed role’s policy

  3. Use Session Duration: Set appropriate session duration limits:

    {
      "Action": "sts:AssumeRole",
      "Condition": {
        "NumericLessThan": {
          "aws:RequestedRegion": "3600"
        }
      }
    }
    
  4. Monitor AssumeRole Calls: Enable CloudTrail to audit all AssumeRole operations

  5. Use IAM Paths: Organize roles using IAM paths for better management:

    arn:aws:iam::111111111111:role/production/app/Role_A
    

Conclusion

Configuring IAM Role to Role AssumeRole is a powerful feature for implementing secure, cross-account access patterns. The key difference between same-account and cross-account scenarios is:

  • Same Account: Only requires Trust Relationship configuration on the target role
  • Cross-Account: Requires both Trust Relationship on target role AND sts:AssumeRole policy on source role

Understanding this distinction is crucial for troubleshooting access issues and implementing secure IAM architectures.

References