Prashant
Prashant

Reputation: 4023

How to assume an AWS role from another AWS role?

I have two AWS account - lets say A and B.

In account B, I have a role defined that allow access to another role from account A. Lets call it Role-B

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

In account A, I have defined a role that allows the root user to assume role. Lets call it Role-A

{
  "Version": "2012-10-17",
  "Statement": [
  {
     "Effect": "Allow",
     "Principal": {
         "AWS": "arn:aws:iam::********:root"
     },
    "Action": "sts:AssumeRole"
  }]
}

Role A has the following policy attached to it

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

As a user in account A, I assumed the Role-A. Now using this temporary credential, I want to assume the Role-B and access the resource owned by account B. I have the below code

client = boto3.client('sts')

firewall_role_object = client.assume_role(
    RoleArn=INTERMEDIARY_IAM_ROLE_ARN,
    RoleSessionName=str("default"),
    DurationSeconds=3600)

firewall_credentials = firewall_role_object['Credentials']

firewall_client = boto3.client(
    'sts',
    aws_access_key_id=firewall_credentials['AccessKeyId'],
    aws_secret_access_key=firewall_credentials['SecretAccessKey'],
    aws_session_token=firewall_credentials['SessionToken'], )

optimizely_role_object = firewall_client.assume_role(
    RoleArn=CUSTOMER_IAM_ROLE_ARN,
    RoleSessionName=str("default"),
    DurationSeconds=3600)

print(optimizely_role_object['Credentials'])

This code works for the set of roles I got from my client but is not working for the roles I defined between two of the AWS account I have access to.

Upvotes: 93

Views: 53243

Answers (2)

Pronex
Pronex

Reputation: 359

For those looking at this question even after so many years... I had the same requirement of having to assume a role through another role and generalized the case somewhat.

With the policies as above, the following block allows assuming a role via a different role:

def assume_role(account_id, role_name, *, session_name=None, transient_role_credentials=None):
    """
    Assume role in an account and return credentials

    Args:
        account_id (str): ID of the account to assume role in
        role_name (str): Name of the role to assume
        session_name (str): optional name for the assume_role session
        transient role (dict): result of a different assume_role call with credentials of the transient role
    
    Returns:
        dict: Credentials for the assumed role
    """
    # set up boto3 client to assume role adding transient role credentials if provided
    if not transient_role_credentials:
        sts_client = boto3.client('sts')
    else:
        sts_client = boto3.client('sts', **transient_role_credentials)

    # generate random session name if not provided
    if not session_name:
        session_name = f'AssumeRoleSession{random.randint(1, 1000)}'

    response = sts_client.assume_role(
        RoleArn=f'arn:aws:iam::{account_id}:role/{role_name}',
        RoleSessionName=session_name,
    )

    creds = {
        'aws_access_key_id': response['Credentials']['AccessKeyId'],
        'aws_secret_access_key': response['Credentials']['SecretAccessKey'],
        'aws_session_token': response['Credentials']['SessionToken']
    }

    return creds

So you can call the function two times in a row:

# get credentials for the transient role
transient_role_creds = assume_role(os.getenv('TRANSIENT_ROLE_ACCOUNT_ID'), os.getenv('TRANSIENT_ROLE_NAME'))
# get credentials for the root account using the transient role credentials
root_creds = assume_role(os.getenv('ROOT_ACCOUNT_ID'), os.getenv('ROOT_ROLE_NAME'),
                         transient_role_credentials=transient_role_creds)

# example usage in a boto3 client
client_cache.get_client('sso-admin', **root_creds)

Upvotes: 2

Prashant
Prashant

Reputation: 4023

Finally got this working. The above configuration is correct. There was a spelling mistake in the policy.

I will keep this question here for it may help someone who want to achieve double hop authentication using roles.

Upvotes: 77

Related Questions