AWS: Boto3:包括角色使用的AssumeRole示例

63

我正在尝试使用AssumeRole以便遍历多个账户并检索这些账户的资产。我已经达到了这一步:

import boto3
stsclient = boto3.client('sts')

assumedRoleObject = sts_client.assume_role(
RoleArn="arn:aws:iam::account-of-role-to-assume:role/name-of-role",
RoleSessionName="AssumeRoleSession1")

太好了,我得到了assumedRoleObject。但是现在我想使用它来列出诸如ELB之类的东西,而不是内置低级资源。

如何做到这一点?如果可以的话 - 请编写一个完整的示例代码,以便每个人都能受益。

11个回答

80

这是从AWS官方文档中的一段代码片段,用于创建一个s3资源来列出所有s3存储桶。其他服务的boto3资源或客户端可以以类似的方式构建。

# create an STS client object that represents a live connection to the 
# STS service
sts_client = boto3.client('sts')

# Call the assume_role method of the STSConnection object and pass the role
# ARN and a role session name.
assumed_role_object=sts_client.assume_role(
    RoleArn="arn:aws:iam::account-of-role-to-assume:role/name-of-role",
    RoleSessionName="AssumeRoleSession1"
)

# From the response that contains the assumed role, get the temporary 
# credentials that can be used to make subsequent API calls
credentials=assumed_role_object['Credentials']

# Use the temporary credentials that AssumeRole returns to make a 
# connection to Amazon S3  
s3_resource=boto3.resource(
    's3',
    aws_access_key_id=credentials['AccessKeyId'],
    aws_secret_access_key=credentials['SecretAccessKey'],
    aws_session_token=credentials['SessionToken'],
)

# Use the Amazon S3 resource object that is now configured with the 
# credentials to access your S3 buckets. 
for bucket in s3_resource.buckets.all():
    print(bucket.name)

assumed_role_object 实际上只是一个字典,其中包含 CredentialsAssumedRoleUserResponseMetadata 的子字典。 - John Mee
如何将s3_resource传递给s3存储桶中的上传文件。我正在使用blob.upload。还有其他不同的方法吗? - Mia

31

获取扮演角色后的会话:

import botocore
import boto3
import datetime
from dateutil.tz import tzlocal

assume_role_cache: dict = {}
def assumed_role_session(role_arn: str, base_session: botocore.session.Session = None):
    base_session = base_session or boto3.session.Session()._session
    fetcher = botocore.credentials.AssumeRoleCredentialFetcher(
        client_creator = base_session.create_client,
        source_credentials = base_session.get_credentials(),
        role_arn = role_arn,
        extra_args = {
        #    'RoleSessionName': None # set this if you want something non-default
        }
    )
    creds = botocore.credentials.DeferredRefreshableCredentials(
        method = 'assume-role',
        refresh_using = fetcher.fetch_credentials,
        time_fetcher = lambda: datetime.datetime.now(tzlocal())
    )
    botocore_session = botocore.session.Session()
    botocore_session._credentials = creds
    return boto3.Session(botocore_session = botocore_session)

# usage:
session = assumed_role_session('arn:aws:iam::ACCOUNTID:role/ROLE_NAME')
ec2 = session.client('ec2') # ... etc.

生成的会话凭据将在需要时自动刷新,这非常好。

注意:我的先前答案完全错误,但我不能删除它,所以我已经用更好、可行的答案替换了它。


2
为什么这是被接受的解决方案,如果它并不起作用? - Mike Conigliaro
别说 AWSSDK 了,就因为在 boto3.session.Session 中我没看到叫做 role_arn 的参数,这很可能会导致出现 python 错误 - y2k-shubham
这里assume_role_cache是如何工作的? - a0s
boto3 1.28.29 - 这个解决方案不起作用 - mysiar

22
您可以使用STS令牌来扮演角色,例如:
class Boto3STSService(object):
    def __init__(self, arn):
        sess = Session(aws_access_key_id=ARN_ACCESS_KEY,
                       aws_secret_access_key=ARN_SECRET_KEY)
        sts_connection = sess.client('sts')
        assume_role_object = sts_connection.assume_role(
            RoleArn=arn, RoleSessionName=ARN_ROLE_SESSION_NAME,
            DurationSeconds=3600)
        self.credentials = assume_role_object['Credentials']

这将为您提供临时的访问密钥和秘密密钥,以及会话令牌。使用这些临时凭据,您可以访问任何服务。例如,如果您想要访问ELB,可以使用以下代码:

self.tmp_credentials = Boto3STSService(arn).credentials

def get_boto3_session(self):
    tmp_access_key = self.tmp_credentials['AccessKeyId']
    tmp_secret_key = self.tmp_credentials['SecretAccessKey']
    security_token = self.tmp_credentials['SessionToken']

    boto3_session = Session(
        aws_access_key_id=tmp_access_key,
        aws_secret_access_key=tmp_secret_key, aws_session_token=security_token
    )
    return boto3_session

def get_elb_boto3_connection(self, region):
    sess = self.get_boto3_session()
    elb_conn = sess.client(service_name='elb', region_name=region)
    return elb_conn

1
工作得很好,尽管我不得不调整一些东西,例如Session是boto3.session.Session。 - Mercury

10

关于@jarrad提出的解决方法,该方法在2021年2月已经无法使用。为了避免显式地使用STS,请参考以下解决方案。


import boto3
import botocore.session
from botocore.credentials import AssumeRoleCredentialFetcher, DeferredRefreshableCredentials


def get_boto3_session(assume_role_arn=None):
    session = boto3.Session(aws_access_key_id="abc", aws_secret_access_key="def")
    if not assume_role_arn:
        return session

    fetcher = AssumeRoleCredentialFetcher(
        client_creator=_get_client_creator(session),
        source_credentials=session.get_credentials(),
        role_arn=assume_role_arn,
    )
    botocore_session = botocore.session.Session()
    botocore_session._credentials = DeferredRefreshableCredentials(
        method='assume-role',
        refresh_using=fetcher.fetch_credentials
    )

    return boto3.Session(botocore_session=botocore_session)


def _get_client_creator(session):
    def client_creator(service_name, **kwargs):
        return session.client(service_name, **kwargs)

    return client_creator

该函数可以按照以下方式调用

ec2_client = get_boto3_session(role_arn='my_role_arn').client('ec2', region_name='us-east-1')

请添加一个用法示例。另外,为什么要使用botocore?是否可能摆脱它并只使用boto3? 请注意,机器翻译会影响到翻译质量,因此我会尽力使翻译流畅自然。例子: 请问您是否知道如何使用Python编写Lambda函数?我正在使用boto3和botocore来管理AWS资源。但是,我想知道是否可以仅使用boto3而不需要botocore。 - maslick
1
botocore具有低级API的实现和接口,其中包括凭证获取器接口。如果您不想显式导入botocore,我建议使用sts client并获取临时凭证。如果您担心额外安装botocore,那么我想指出boto3是基于botocore构建的,并且始终会安装botocore https://github.com/boto/boto3/blob/develop/setup.py#L16。 - Sar009
非常好!谢谢! - maslick

7

这是我用的代码片段

sts_client = boto3.client('sts')
assumed_role_object = sts_client.assume_role(
    RoleArn=<arn of the role to assume>,
    RoleSessionName="<role session name>"
)
print(assumed_role_object)
credentials = assumed_role_object['Credentials']
session = Session(
    aws_access_key_id=credentials['AccessKeyId'],
    aws_secret_access_key=credentials['SecretAccessKey'],
    aws_session_token=credentials['SessionToken']
)
self.s3 = session.client('s3')

2
这段代码更短,更易于理解。 - Brian
但是如果没有适当的进口,它是无法正常工作的。 - undefined

7
如果您想要一个功能实现,这是我采用的方案:
def filter_none_values(kwargs: dict) -> dict:
    """Returns a new dictionary excluding items where value was None"""
    return {k: v for k, v in kwargs.items() if v is not None}


def assume_session(
    role_session_name: str,
    role_arn: str,
    duration_seconds: Optional[int] = None,
    region_name: Optional[str] = None,
) -> boto3.Session:
    """
    Returns a session with the given name and role.
    If not specified, duration will be set by AWS, probably at 1 hour.
    If not specified, region will be left unset.
    Region can be overridden by each client or resource spawned from this session.
    """
    assume_role_kwargs = filter_none_values(
        {
            "RoleSessionName": role_session_name,
            "RoleArn": role_arn,
            "DurationSeconds": duration_seconds,
        }
    )
    credentials = boto3.client("sts").assume_role(**assume_role_kwargs)["Credentials"]
    create_session_kwargs = filter_none_values(
        {
            "aws_access_key_id": credentials["AccessKeyId"],
            "aws_secret_access_key": credentials["SecretAccessKey"],
            "aws_session_token": credentials["SessionToken"],
            "region_name": region_name,
        }
    )
    return boto3.Session(**create_session_kwargs)


def main() -> None:
    session = assume_session(
        "MyCustomSessionName",
        "arn:aws:iam::XXXXXXXXXXXX:role/TheRoleIWantToAssume",
        region_name="us-east-1",
    )
    client = session.client(service_name="ec2")
    print(client.describe_key_pairs())


这个错误提示是:在调用AssumeRole操作时发生了错误(InvalidClientTokenId):请求中包含的安全令牌无效。 - shredding
首先获取扮演角色的响应,然后检查结果,而不是像答案所做的那样尝试一次性完成所有操作。您可能会发现扮演角色的结果因其他原因而失败。 - MikeW

4
#!/usr/bin/env python3

import boto3

sts_client = boto3.client('sts')
assumed_role = sts_client.assume_role(RoleArn =  "arn:aws:iam::123456789012:role/example_role",
                                      RoleSessionName = "AssumeRoleSession1",
                                      DurationSeconds = 1800)
session = boto3.Session(
    aws_access_key_id     = assumed_role['Credentials']['AccessKeyId'],
    aws_secret_access_key = assumed_role['Credentials']['SecretAccessKey'],
    aws_session_token     = assumed_role['Credentials']['SessionToken'],
    region_name           = 'us-west-1'
)

# now we make use of the role to retrieve a parameter from SSM
client = session.client('ssm')
response = client.get_parameter(
    Name = '/this/is/a/path/parameter',
    WithDecryption = True
)
print(response)

1
import json
import boto3


roleARN = 'arn:aws:iam::account-of-role-to-assume:role/name-of-role'
client = boto3.client('sts')
response = client.assume_role(RoleArn=roleARN, 
                              RoleSessionName='RoleSessionName', 
                              DurationSeconds=900)

dynamodb_client = boto3.client('dynamodb', region_name='us-east-1',
                    aws_access_key_id=response['Credentials']['AccessKeyId'],
                    aws_secret_access_key=response['Credentials']['SecretAccessKey'],
                    aws_session_token = response['Credentials']['SessionToken'])

response = dynamodb_client.get_item(
Key={
    'key1': {
        'S': '1',
    },
    'key2': {
        'S': '2',
    },
},
TableName='TestTable')
print(response)

1
假设 1) ~/.aws/config~/.aws/credentials 文件中填写了您希望扮演的每个角色,并且 2) 默认角色在其 IAM 策略中为这些角色定义了 AssumeRole,则您可以简单地执行以下伪代码,而无需使用 STS:
import boto3

# get all of the roles from the AWS config/credentials file using a config file parser
profiles = get_profiles()

for profile in profiles:

    # this is only used to fetch the available regions
    initial_session = boto3.Session(profile_name=profile)

    # get the regions
    regions = boto3.Session.get_available_regions('ec2')

    # cycle through the regions, setting up session, resource and client objects
    for region in regions:
        boto3_session = boto3.Session(profile_name=profile, region_name=region)
        boto3_resource = boto3_session.resource(service_name='s3', region_name=region)
        boto3_client = boto3_session.client(service_name='s3', region_name=region)

        [ do something interesting with your session/resource/client here ]


1
非常感谢,帮了大忙... - namik

0

经过几天的搜索,我找到了最简单的解决方案。这里有详细解释,但没有使用示例。

import boto3


for profile in boto3.Session().available_profiles:

    boto3.DEFAULT_SESSION = boto3.session.Session(profile_name=profile)

    s3 = boto3.resource('s3')

    for bucket in s3.buckets.all():
        print(bucket)

这将切换您将使用的默认角色。如果不想将配置文件设置为默认值,只需不将其分配给boto3.DEFAULT_SESSION,而是执行以下操作。

testing_profile = boto3.session.Session(profile_name='mainTesting')
s3 = testing_profile.resource('s3')

for bucket in s3.buckets.all():
    print(bucket)

需要注意的是,.aws凭证需要以特定的方式设置。

[default]
aws_access_key_id = default_access_id
aws_secret_access_key = default_access_key

[main]
aws_access_key_id = main_profile_access_id
aws_secret_access_key = main_profile_access_key

[mainTesting]
source_profile = main
role_arn = Testing role arn
mfa_serial = mfa_arn_for_main_role

[mainProduction]
source_profile = main
role_arn = Production role arn
mfa_serial = mfa_arn_for_main_role

我不知道为什么,但是mfa_serial密钥必须在角色上才能起作用,而不是在源帐户上,这似乎更合理。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接