Reputation: 1446
What am I doing wrong here? I'm able to assume the roles via aws cli as well as boto if I use:
boto3.setup_default_session(profile_name="ROLE_TO_ASSUME")
What I'm trying to do: I have a number of AWS accounts my script needs to run in. I'm tired of typing my mfa each time I need to run the script on a different profile/role.
I get this error message with the code below:
User: arn:aws:iam::<management account>:user/Ops/<my user> is not authorized to perform: sts:AssumeRole on resource
Our AWS is setup like this: I am a user, part of a group on a management account. The group has a trust relationship set up with the ROLE_TO_ASSUME role on each account.
Here's my python:
import boto3
def main():
boto3.setup_default_session(profile_name="default")
ec2 = boto3.client('ec2')
get_assumerole_credentials('arn:aws:iam::<REPLACE WITH ACCOUNTID>:role/ROLE_TO_ASSUME')
def get_assumerole_credentials(arn):
sts_client = boto3.client('sts')
# Use client object and pass the role ARN
assumedRoleObject = sts_client.assume_role(RoleArn=arn,
RoleSessionName="AssumeRoleCredstashSession1")
credentials = assumedRoleObject['Credentials']
return dict(aws_access_key_id=credentials['AccessKeyId'],
aws_secret_access_key=credentials['SecretAccessKey'],
aws_session_token=credentials['SessionToken'])
if __name__ == "__main__":
main()
Here's my ~/.aws/config
[profile default]
region = us-west-2
output = json
aws_access_key_id=<censored>
aws_secret_access_key=<censored>
[profile ROLE_TO_ASSUME]
region = us-west-2
source_profile = default
role_arn = arn:aws:iam::<accountid>:role/ROLE_TO_ASSUME
mfa_serial = arn:aws:iam::<accountid>:mfa/<my_user>
EDIT based on first reply:
To be clear, I am able to assume a role if I specify the 'profile' argument as in the following example:
boto3.setup_default_session(profile_name='ROLE_TO_ASSUME')
ec2 = boto3.resource('ec2', region_name='us-west-1')
But I need to assume a role and within the script using boto3's STS to get temp credentials.
I've noticed there is no MFA prompt when I use the boto3 STS assume role method of connecting.
Upvotes: 3
Views: 12147
Reputation: 31
import boto3
mfa=raw_input()
hours_required=2
device_id=<arn of mfa device>
sts_client=boto3.client('sts')
credentials=sts_client.get_session_token(DurationSeconds=hours_required*60*60,SerialNumber=device_id,TokenCode=mfa)
session=boto3.session.Session(
aws_access_key_id=credentials['Credentials']['AccessKeyId'],
aws_secret_access_key=credentials['Credentials']['SecretAccessKey'],
aws_session_token=credentials['Credentials']['SessionToken']
)
now you can use session variable and use it to create sts client, and call assume role on that object.
Upvotes: 2
Reputation: 1925
All other solutions here do not refresh the assumed credentials. But they do expire (in 15 minutes by default, but you can set it longer when doing the AssumeRole call, up to your current session max API session duration time, by default -- 1 hour).
If you need to automatically refresh the credentials, here I'm sharing my code I wrote after several hours of studying boto's code and trying to work around boto's dependency on file system and profiles.
Here I just used built-in boto's mechanism of caching and regularly refreshing the assumed credentials without touching any files:
from datetime import datetime
import boto3
from botocore.credentials import (
AssumeRoleProvider,
AssumeRoleCredentialFetcher,
DeferredRefreshableCredentials,
CredentialResolver
)
from dateutil.tz import tzlocal
class RamAssumeRoleProvider(AssumeRoleProvider):
"""
Overrides default AssumeRoleProvider to not use profiles from filesystem.
"""
def __init__(self,
source_session: boto3.Session,
assume_role_arn: str,
expiry_window_seconds: int):
super().__init__(
load_config=lambda: source_session._session.full_config,
client_creator=source_session._session.create_client,
cache={},
profile_name='not-used'
)
self.expiry_window_seconds = expiry_window_seconds
self.source_session = source_session
self.assume_role_arn = assume_role_arn
assert assume_role_arn, "assume_role_arn is required"
def load(self):
fetcher = AssumeRoleCredentialFetcher(
client_creator=self.source_session._session.create_client,
source_credentials=self.source_session.get_credentials(),
role_arn=self.assume_role_arn,
expiry_window_seconds=self.expiry_window_seconds,
cache=self.cache,
)
return DeferredRefreshableCredentials(
method=self.METHOD,
refresh_using=fetcher.fetch_credentials,
time_fetcher=lambda: datetime.now(tzlocal())
)
def get_assume_role_session(
source_session: boto3.Session,
assume_role_arn: str,
expiry_window_seconds=15 * 60
) -> boto3.Session:
"""
Creates a new boto3 session that will operate as of another user.
Source session must have permission to call sts:AssumeRole on the provided ARN,
and that ARN role must have been trusted to be assumed from this account (where source_session is from).
See https://docs.aws.amazon.com/IAM/latest/UserGuide/tutorial_cross-account-with-roles.html
Uses internal session._session to hack it together, as I haven't found another way.
"""
# must have .load() method to be used in CredentialsResolver.
provider = RamAssumeRoleProvider(
source_session=source_session,
assume_role_arn=assume_role_arn,
expiry_window_seconds=expiry_window_seconds
)
# must have .load_credentials() method to be used in register_component()
resolver = CredentialResolver([provider])
new_session = boto3.Session()
new_session._session.register_component('credential_provider', resolver)
return new_session
Upvotes: 2
Reputation: 3
I think if we need to assume role - this is what I did and it automatically assumed role. Steps (Windows/Unix):
Within the directory, create a file name called credentials (with no extension) Within credentials, enter
[default]
aws_access_key_id=AKIAIOSFODNN7EXAMPLE
aws_secret_access_key=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
Within the same directory .aws, create a file name called config (with no extension) and enter the following information
[default]
region=us-west-2
role_arn=arn:aws:iam::
source_profile=default
import boto3
client = boto3.client('s3')
boto3 looks at these default location for credentials/config file where it picks up all the access/role information.
Upvotes: 0
Reputation: 1446
import boto3
# Prompt for MFA time-based one-time password (TOTP)
mfa_TOTP = raw_input("Enter the MFA code: ")
def role_arn_to_session(**args):
"""
Usage :
session = role_arn_to_session(
RoleArn='arn:aws:iam::<ACCOUNT_NUMBER>:role/example-role',
RoleSessionName=<'SESSION_NAME'>,
SerialNumber='<ARN_OF_MFA_DEVICE',
TokenCode=mfa_TOTP)
client = session.client('ec2')
"""
client = boto3.client('ec2')
response = client.assume_role(**args)
return boto3.Session(
aws_access_key_id=response['Credentials']['AccessKeyId'],
aws_secret_access_key=response['Credentials']['SecretAccessKey'],
aws_session_token=response['Credentials']['SessionToken'])
Upvotes: 2
Reputation: 6333
I assume the below is working fine for you
boto3.setup_default_session(profile_name='ROLE_TO_ASSUME')
ec2 = boto3.resource('ec2', region_name='us-west-1')
So, to get the STS temp credentials, do the below
boto3.setup_default_session(profile_name='ROLE_TO_ASSUME')
session = boto3.session.Session()
temp_credentials = session.get_credentials().get_frozen_credentials()
Note : This has nothing to do with MFA if first assumption is working fine.
If you are looking for assume role with MFA, refer to assume role with MFA http://boto3.readthedocs.io/en/latest/reference/services/sts.html#STS.Client.assume_role
Upvotes: 2