You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

能否在Lambda触发器中获取AWS Cognito用户属性,实现S3触发发邮件?

解决方案

首先明确:Lambda触发器中完全可以获取AWS Cognito用户属性,你遇到的两个核心问题分别是事件结构解析错误、Lambda执行角色权限缺失,按以下步骤排查实现即可:


1. 解决KeyError: 'Records'报错

这个报错的核心原因是Lambda接收到的入参event中没有Records字段,对应排查方案:

  • 99%的场景是你在Lambda控制台手动测试时使用了默认测试模板,没有替换为S3触发事件,配置测试事件时选择官方提供的s3-put模板即可复现真实触发场景
  • 先在lambda_handler函数第一行添加print(json.dumps(event)),到CloudWatch日志中查看实际收到的入参结构,确认S3上传触发时的事件确实包含Records字段即可排除该问题
  • 你提到的上传S3时能看到监控打点,说明触发逻辑是正常的,报错大概率是测试时用了错误的事件模板导致

2. 解决Cognito list_users接口在Lambda运行失败的问题

本地运行能成功是因为你本地AWS账号密钥有Cognito操作权限,而Lambda运行时依赖绑定的执行角色(Execution Role)的权限,默认没有Cognito访问权限,按以下配置:

  • 打开Lambda控制台的「配置」-「权限」板块,点击执行角色的跳转链接进入IAM角色配置页
  • 给角色添加如下权限策略,替换尖括号中的内容为你实际的资源信息:
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": "cognito-idp:ListUsers",
            "Resource": "arn:aws:cognito-idp:<AWS区域>:<AWS账号ID>:userpool/<Cognito用户池ID>"
        }
    ]
}
  • 确认你代码中的UserPoolId参数已经填入了实际的Cognito用户池ID,不要留空

3. 完整实现S3上传后给所有Cognito用户发邮件的逻辑

发邮件推荐使用AWS SES服务,需要先在SES控制台验证你作为发件方的邮箱,若要发给未验证的收件邮箱需要提交工单解除SES沙箱限制,再给Lambda执行角色添加SES发送权限:

{
    "Effect": "Allow",
    "Action": "ses:SendEmail",
    "Resource": "*"
}

完整代码示例:

import boto3
import urllib.parse
import json

# 初始化AWS客户端
s3 = boto3.client('s3')
cognito_client = boto3.client('cognito-idp')
ses_client = boto3.client('ses', region_name='<SES服务所在区域>')

# 固定配置项,替换为你实际的资源信息
USER_POOL_ID = '<Cognito用户池ID>'
SENDER_EMAIL = '<SES验证过的发件邮箱>'
EMAIL_SUBJECT = 'S3存储桶新文件上传通知'

def lambda_handler(event, context):
    # 打印入参方便排查问题
    print("Received event: ", json.dumps(event))

    # 解析S3触发事件
    try:
        bucket = event['Records'][0]['s3']['bucket']['name']
        key = urllib.parse.unquote_plus(event['Records'][0]['s3']['object']['key'], encoding='utf-8')
    except KeyError as e:
        print(f"Event structure error, missing field: {e}")
        raise e

    # 读取S3文件内容(保留你原有逻辑)
    try:
        s3_resp = s3.get_object(Bucket=bucket, Key=key)
        file_content = s3_resp["Body"].read().decode()
        data = json.loads(file_content)
        print("S3 file content: ", data)
    except Exception as e:
        print(f"Read S3 file failed: {e}")
        raise e

    # 拉取所有Cognito用户的邮箱,处理分页(用户量超过60需要翻页)
    try:
        all_users = []
        pagination_token = None
        while True:
            if pagination_token:
                cognito_resp = cognito_client.list_users(UserPoolId=USER_POOL_ID, PaginationToken=pagination_token)
            else:
                cognito_resp = cognito_client.list_users(UserPoolId=USER_POOL_ID)
            all_users.extend(cognito_resp['Users'])
            pagination_token = cognito_resp.get('PaginationToken')
            if not pagination_token:
                break
        
        # 提取有效邮箱,可根据需求过滤已验证的邮箱
        email_list = []
        for user in all_users:
            for attr in user['Attributes']:
                if attr['Name'] == 'email' and attr['Value']:
                    # 可选校验邮箱是否已验证:if next((a for a in user['Attributes'] if a['Name'] == 'email_verified'), {}).get('Value') == 'true'
                    email_list.append(attr['Value'])
        # 邮箱去重
        email_list = list(set(email_list))
        print("Target email list: ", email_list)
    except Exception as e:
        print(f"Fetch Cognito users failed: {e}")
        raise e

    # 批量发送邮件
    try:
        for email in email_list:
            email_body = f"""
            <p>您好:</p>
            <p>S3存储桶 {bucket} 已上传新文件:{key}</p>
            <p>文件内容摘要:{json.dumps(data, indent=2, ensure_ascii=False)}</p>
            """
            ses_client.send_email(
                Source=SENDER_EMAIL,
                Destination={'ToAddresses': [email]},
                Message={
                    'Subject': {'Data': EMAIL_SUBJECT, 'Charset': 'UTF-8'},
                    'Body': {'Html': {'Data': email_body, 'Charset': 'UTF-8'}}
                }
            )
        print(f"Successfully sent email to {len(email_list)} users")
        return 'Success!'
    except Exception as e:
        print(f"Send email failed: {e}")
        raise e

4. 额外注意事项

  • 如果Cognito用户量超过1000,建议把发邮件逻辑拆分到SQS队列异步处理,避免Lambda运行超时
  • Lambda默认超时时间是3秒,建议调整到30秒以上,避免接口调用超时导致执行失败
  • SES沙箱环境默认只能发给已经验证过的邮箱,需要给任意邮箱发信要提交AWS工单解除沙箱限制

内容的提问来源于stack exchange,提问作者s hyun

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.29 15:06:04