サイトアイコン 協栄情報ブログ

CloudFormation を使用して、既存の S3 バケットで Lambda 用の Amazon S3 通知設定を作成してみました。


この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので十分ご注意ください。

前提

・イベント通知したいS3バケットが作成済みであること
・CloudFormationのテンプレートをyaml形式で作成する

手順

CloudFormationテンプレートを使って、AWS CloudFormationコンソール上でスタックの作成手順を説明します。

CloudFormationテンプレート

S3EventNotification.yaml

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
    Default: ''
  Prefix:
    Type: String
    Description: Prefix of the object key name for filtering rules
    Default: 'test_prefix'
  Suffix:
    Type: String
    Description: Suffix of the object key name for filtering rules
    Default: 'txt'

Resources:
  S3NotificationLambdaFunction:
    Type: 'AWS::Lambda::Function'
    Properties:
      Code:
        ZipFile: |
          import json
          def lambda_handler(event,context):
              bucket = event['Records'][0]['s3']['bucket']['name']
              key = event['Records'][0]['s3']['object']['key']
              print(key+' has been created '+'in Bucket '+bucket)
      Handler: index.lambda_handler
      Role: !GetAtt LambdaIAMRole.Arn
      Runtime: python3.9
      Timeout: 5

  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:*:*:*'

  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}'

  LambdaTrigger:
    Type: 'Custom::LambdaTrigger'
    DependsOn: LambdaInvokePermission
    Properties:
      ServiceToken: !GetAtt CustomResourceLambdaFunction.Arn
      Id: !Sub
        - S3LambdaNotif-${UniqueId}
        - UniqueId: !Select [0, !Split ['-', !Select [2, !Split [/, !Ref 'AWS::StackId']]]]
      Bucket: !Ref NotificationBucket
      Prefix: !Ref Prefix
      Suffix: !Ref Suffix
      LambdaArn: !GetAtt S3NotificationLambdaFunction.Arn

  CustomResourceLambdaFunction:
    Type: 'AWS::Lambda::Function'
    Properties:
      Handler: index.lambda_handler
      Role: !GetAtt LambdaIAMRole.Arn
      Code:
        ZipFile: |
          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['RequestType'] == 'Delete':
                      print("Request Type:",event['RequestType'])
                      Id=event['ResourceProperties']['Id']
                      Bucket=event['ResourceProperties']['Bucket']

                      delete_notification(Id, Bucket)
                      print("Sending response to custom resource after Delete")
                  elif event['RequestType'] == 'Create' or event['RequestType'] == 'Update':
                      print("Request Type:",event['RequestType'])
                      Id=event['ResourceProperties']['Id']
                      Prefix=event['ResourceProperties']['Prefix']
                      Suffix=event['ResourceProperties']['Suffix']
                      LambdaArn=event['ResourceProperties']['LambdaArn']
                      Bucket=event['ResourceProperties']['Bucket']

                      add_notification(Id, Prefix, Suffix, LambdaArn, Bucket)
                      responseData={'Bucket':Bucket}
                      print("Sending response to custom resource")
                  responseStatus = 'SUCCESS'
              except Exception as e:
                  print('Failed to process:', e)
                  responseStatus = 'FAILED'
                  responseData = {'Failure': 'Something bad happened.'}
              cfnresponse.send(event, context, responseStatus, responseData)

          def add_notification(Id, Prefix, Suffix, LambdaArn, Bucket):
              bucket_notification = s3.BucketNotification(Bucket)
              print(bucket_notification.lambda_function_configurations)

              lambda_function_configurations = bucket_notification.lambda_function_configurations

              if lambda_function_configurations is None:
                  lambda_function_configurations = []
              else:
                  lambda_function_configurations = [e for e in lambda_function_configurations if e['Id'] != Id]

              lambda_config = {}
              lambda_config['Id'] = Id
              lambda_config['LambdaFunctionArn'] = LambdaArn
              lambda_config['Events'] = ['s3:ObjectCreated:*']
              lambda_config['Filter'] = {'Key': {'FilterRules': [
                      {'Name': 'Prefix', 'Value': Prefix},
                      {'Name': 'Suffix', 'Value': Suffix}
                      ]}
              }

              lambda_function_configurations.append(lambda_config)
              print(lambda_function_configurations)

              put_bucket_notification(bucket_notification, lambda_function_configurations)

              print("Put request completed....")

          def delete_notification(Id, Bucket):

              bucket_notification = s3.BucketNotification(Bucket)
              print(bucket_notification.lambda_function_configurations)

              lambda_function_configurations = bucket_notification.lambda_function_configurations

              if lambda_function_configurations is not None:
                  lambda_function_configurations = [e for e in lambda_function_configurations if e['Id'] != Id]

              print(lambda_function_configurations)

              put_bucket_notification(bucket_notification, lambda_function_configurations)

              print("Delete request completed....")

          def put_bucket_notification(BucketNotification, LambdaFunctionConfigurations):

              notification_configuration = {}
              if LambdaFunctionConfigurations is not None:
                  notification_configuration['LambdaFunctionConfigurations'] = LambdaFunctionConfigurations

              if BucketNotification.queue_configurations is not None:
                  notification_configuration['QueueConfigurations'] = BucketNotification.queue_configurations

              if BucketNotification.topic_configurations is not None:
                  notification_configuration['TopicConfigurations'] = BucketNotification.topic_configurations

              print(notification_configuration)

              response = BucketNotification.put(
                NotificationConfiguration= notification_configuration
              )
      Runtime: python3.9
      Timeout: 50

AWS CloudFormationコンソール上でスタックの作成

AWS コンソールをログインし、CloudFormationを開きます。
その後、「スタックの作成」⇒「新しいリソースを使用(標準)」をクリックし、テンプレートを指定して「次へ」をクリックします。

パラメータを入力して「次へ」をクリックします。

「次へ」をクリックします。

「AWS CloudFormation によって IAM リソースが作成される場合があることを承認します。」をチェックし、「スタックの作成」をクリックします。

作成後の動作確認

スタックのリソースタブを開き、下記のようにLambdaをクリックします。

S3バケットがトリガーに追加されていることを確認します。

S3バケットのトリガー詳細を確認します。

S3バケットにテストファイルをアップロードします。

「モニタリング」をクリックします。

「CloudWatchのログを表示」をクリックします。

「ログストリーム」をクリックし、Lambdaに実装しているログが出力されいることを確認します。

参考

https://aws.amazon.com/jp/premiumsupport/knowledge-center/cloudformation-s3-notification-lambda/

モバイルバージョンを終了