Note that the credentials you obtain from sts.AssumeRole call do expire after some time (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) unless you refresh them.
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:
```py
from datetime import datetime
import boto3
from botocore.credentials import (
AssumeRoleProvider,
AssumeRoleCredentialFetcher,
DeferredRefreshableCredentials,
CredentialResolver
)
from dateutil.tz import tzlocal
class CustomAssumeRoleProvider(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 = CustomAssumeRoleProvider(
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
```
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:
```py
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
```