Reputation: 4023
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
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
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