使用Boto3自动标记AWS实例:获取启动者IAM邮箱报错
问题描述
我想要获取启动AWS EC2实例的IAM用户邮箱和账号ID,并用这些信息给实例打标签。目前账号ID能正常获取,但获取IAM用户邮箱时一直报错。
这个Lambda函数由CloudWatch事件规则触发,当实例状态变为running时,会把实例ID传递给Lambda函数。
现有代码
import boto3 def lambda_handler(event, context): print(event) # 获取事件中的EC2实例ID instance_id = event['detail']['instance-id'] # 获取账号ID sts_client = boto3.client('sts') identity = sts_client.get_caller_identity() account_id = identity['Account'] # 给EC2实例打上邮箱和账号ID标签 ec2 = boto3.client('ec2') # 描述实例以获取IAM角色ARN response = ec2.describe_instances(InstanceIds=[instance_id]) iam_role_arn = response['Reservations'][0]['Instances'][0]['IamInstanceProfile']['Arn'] # 获取IAM客户端 iam = boto3.client('iam') # 从IAM角色ARN中提取角色名称 role_name = iam_role_arn.split('/')[1] # 获取角色详情 role_details = iam.get_role(RoleName=role_name) # 从角色详情中获取策略ARN policy_arn = role_details['Role']['AssumeRolePolicyDocument']['Statement'][0]['Principal']['AWS'][0] # 获取策略详情 policy_details = iam.get_policy(PolicyArn=policy_arn) # 从策略详情中获取用户ARN user_arn = policy_details['Policy']['UserName'] # 获取用户详情 user_response = iam.get_user(UserName=user_arn) # 从用户详情中获取用户邮箱 user_email = user_response['User']['UserName'] ec2.create_tags( Resources=[instance_id], Tags=[ { 'Key': 'Email', 'Value': email }, { 'Key': 'AccountID', 'Value': user_email } ] )
问题分析与修复方案
原代码中的核心错误
- 变量引用混乱:打标签时用了未定义的
email变量,且AccountID标签错误地使用user_email作为值,应该用获取到的account_id。 - IAM用户邮箱逻辑错误:
- 实例关联角色的信任策略
Principal.AWS不一定是IAM用户,可能是账号根用户、其他角色或服务主体,直接取这个作为用户ARN完全错误。 get_policy返回的Policy对象不存在UserName字段,这一行必然触发报错。- IAM用户的邮箱不会默认出现在
get_user结果中,通常需要从用户标签或登录配置中读取。
- 实例关联角色的信任策略
- 获取启动用户的方式错误:通过实例关联角色反推启动用户的逻辑不成立,正确方式是查询CloudTrail记录的
RunInstances事件,该事件会记录启动实例的调用者信息。
修正后的代码
以下是调整后的Lambda函数,通过CloudTrail查询启动实例的用户,并从用户标签中提取邮箱(需提前给IAM用户设置Email标签):
import boto3 from datetime import datetime, timedelta def lambda_handler(event, context): print(event) instance_id = event['detail']['instance-id'] # 获取账号ID sts_client = boto3.client('sts') identity = sts_client.get_caller_identity() account_id = identity['Account'] # 初始化CloudTrail客户端,查询启动实例的事件 cloudtrail = boto3.client('cloudtrail') start_time = datetime.utcnow() - timedelta(hours=1) end_time = datetime.utcnow() response = cloudtrail.lookup_events( LookupAttributes=[ {'AttributeKey': 'ResourceName', 'AttributeValue': instance_id}, {'AttributeKey': 'EventName', 'AttributeValue': 'RunInstances'} ], StartTime=start_time, EndTime=end_time, MaxResults=1 ) if not response['Events']: print(f"未找到启动实例{instance_id}的RunInstances事件") return # 提取调用者用户名 caller_arn = response['Events'][0]['Username'] user_name = caller_arn.split('/')[-1] # 获取IAM用户详情及标签 iam = boto3.client('iam') user_response = iam.get_user(UserName=user_name) user_tags = user_response['User'].get('Tags', []) # 从标签中获取邮箱,无标签则用用户名作为备选 user_email = None for tag in user_tags: if tag['Key'] == 'Email': user_email = tag['Value'] break user_email = user_email or user_name # 给实例打标签 ec2 = boto3.client('ec2') ec2.create_tags( Resources=[instance_id], Tags=[ {'Key': 'Email', 'Value': user_email}, {'Key': 'AccountID', 'Value': account_id} ] ) print(f"成功给实例{instance_id}打标签:Email={user_email}, AccountID={account_id}")
必要配置说明
- Lambda权限:需为Lambda角色添加以下权限:
sts:GetCallerIdentitycloudtrail:LookupEventsiam:GetUserec2:CreateTags
- CloudTrail启用:确保账号已启用CloudTrail,且覆盖实例所在区域
- IAM用户标签:提前给IAM用户添加
Email标签,否则会用用户名作为邮箱备选值
内容的提问来源于stack exchange,提问作者toluwalase adedotun
相关产品推荐
相关产品推荐

