如何使用 CloudFormation 在现有 S3 存储桶上为 Lambda 创建 Amazon S3 通知配置?

上次更新日期:2021 年 3 月 2 日

我想要使用现有的 Amazon Simple Storage Service (Amazon S3) 存储桶为使用 AWS CloudFormation 的 AWS Lambda 函数创建 Amazon S3 通知配置。

简短描述

要创建 Amazon S3 通知配置,可以使用 CloudFormation 创建新的 S3 存储桶。然后,使用 NotificationConfiguration 属性在该存储桶中添加通知配置。或者,在现有 S3 存储桶中手动添加通知配置。

以下步骤将向您介绍如何通过 Python 3.6 中创建的 Lambda 支持的自定义资源,使用 CloudFormation 在现有 S3 存储桶中添加通知配置。自定义资源会触发 Lambda 函数,该函数将触发 PutBucketNotification API 在您的 S3 存储桶中添加通知配置。

注意:如果您在运行 AWS 命令行界面 (AWS CLI) 命令时遇到错误,请确保您使用的是最新版的 AWS CLI。

解决方法

重要提示:以下步骤仅适用于不具有现有通知配置的 S3 存储桶的 S3 通知配置。如果您的 S3 存储桶已有现有的或手动创建的通知配置,以下步骤将覆盖其配置。

1.创建一个包含以下代码的名为 LambdaS3.template 的 CloudFormation 模板。

重要提示:在下面的示例中,您将 S3 通知配置添加到 S3NotificationLambdaFunction 资源。您使用 Lambda 函数 CustomResourceLambdaFunctionS3NotificationLambdaFunction 添加 S3 通知配置。要满足您的要求,您可以修改 CustomResourceLambdaFunction 资源中的代码。

AWSTemplateFormatVersion: 2010-09-09
Description: >-
  Sample template to illustrate use of existing S3 bucket as an event source for a Lambda function
Parameters:
  NotificationBucket:
    Type: String
    Description: S3 bucket that's used for the Lambda event notification

Resources:
  S3NotificationLambdaFunction:
    Type: 'AWS::Lambda::Function'
    Properties:
      Code:
        ZipFile: !Join
          - |+

          - - import json
            - 'def lambda_handler(event,context):'
            - '    return ''Welcome... This is a test Lambda Function'''
      Handler: index.lambda_handler
      Role: !GetAtt LambdaIAMRole.Arn
      Runtime: python3.6
      Timeout: 5

  LambdaInvokePermission:
    Type: 'AWS::Lambda::Permission'
    Properties:
      FunctionName: !GetAtt S3NotificationLambdaFunction.Arn
      Action: 'lambda:InvokeFunction'
      Principal: s3.amazonaws.com
      SourceAccount: !Ref 'AWS::AccountId'
      SourceArn: !Sub 'arn:aws:s3:::${NotificationBucket}'

  LambdaIAMRole:
    Type: 'AWS::IAM::Role'
    Properties:
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Principal:
              Service:
                - lambda.amazonaws.com
            Action:
              - 'sts:AssumeRole'
      Path: /
      Policies:
        - PolicyName: root
          PolicyDocument:
            Version: 2012-10-17
            Statement:
              - Effect: Allow
                Action:
                  - 's3:GetBucketNotification'
                  - 's3:PutBucketNotification'
                Resource: !Sub 'arn:aws:s3:::${NotificationBucket}'
              - Effect: Allow
                Action:
                  - 'logs:CreateLogGroup'
                  - 'logs:CreateLogStream'
                  - 'logs:PutLogEvents'
                Resource: 'arn:aws:logs:*:*:*'

  CustomResourceLambdaFunction:
    Type: 'AWS::Lambda::Function'
    Properties:
      Handler: index.lambda_handler
      Role: !GetAtt LambdaIAMRole.Arn
      Code:
        ZipFile: |

            from __future__ import print_function
            import json
            import boto3
            import cfnresponse
            
            SUCCESS = "SUCCESS"
            FAILED = "FAILED"
            
            print('Loading function')
            s3 = boto3.resource('s3')
            
            def lambda_handler(event, context):
                print("Received event: " + json.dumps(event, indent=2))
                responseData={}
                try:
                    if event == 'Delete':
                        print("Request Type:",event)
                        Bucket=event
                        delete_notification(Bucket)
                        print("Sending response to custom resource after Delete")
                    elif event == 'Create' or event == 'Update':
                        print("Request Type:",event)
                        LambdaArn=event
                        Bucket=event
                        add_notification(LambdaArn, Bucket)
                        responseData={'Bucket':Bucket}
                        print("Sending response to custom resource")
                    responseStatus = 'SUCCESS'
                except Exception as e:
                    print('Failed to process:', e)
                    responseStatus = 'FAILURE'
                    responseData = {'Failure': 'Something bad happened.'}
                cfnresponse.send(event, context, responseStatus, responseData)

            def add_notification(LambdaArn, Bucket):
                bucket_notification = s3.BucketNotification(Bucket)
                response = bucket_notification.put(
                  NotificationConfiguration={
                    'LambdaFunctionConfigurations': 
                      }
                    ]
                  }
                )
                print("Put request completed....")
              
            def delete_notification(Bucket):
                bucket_notification = s3.BucketNotification(Bucket)
                response = bucket_notification.put(
                    NotificationConfiguration={}
                )
                print("Delete request completed....")
      Runtime: python3.6
      Timeout: 50

  LambdaTrigger:
    Type: 'Custom::LambdaTrigger'
    DependsOn: LambdaInvokePermission
    Properties:
      ServiceToken: !GetAtt CustomResourceLambdaFunction.Arn
      LambdaArn: !GetAtt S3NotificationLambdaFunction.Arn
      Bucket: !Ref NotificationBucket

2.要使用 LambdaS3.template 文件启动 CloudFormation 堆栈,请使用 CloudFormation 控制台或以下 AWS CLI 命令:

aws cloudformation create-stack --stack-name lambda-s3-notification --template-body file://LambdaS3.template --parameters ParameterKey=NotificationBucket,ParameterValue=existing-bucket-for-lambda-notification --capabilities CAPABILITY_NAMED_IAM --region us-east-1

重要提示:当您启动 CloudFormation 堆栈时,必须传递您的 S3 存储桶 (existing-bucket-for-lambda-notification)。

该堆栈将为 Amazon S3 创建 Lambda 函数和 Lambda 权限。现在,您可以使用 S3 存储桶来处理 Lambda 通知,因为该堆栈已在您的 S3 存储桶中添加了所需的通知配置。