2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

AWS CloudFormation: 06. Lambda関数を作成して、S3/EventBridge/SQS から呼び出す

Last updated at Posted at 2023-08-18

本記事について

S3へのアップロードをトリガーとして呼び出す

構築するアーキテクチャ

image.png

  • まず、Lambda関数用のIAMロールを作成します
    • Lambda用に基本的な AWSLambdaBasicExecutionRole を付与し、それ以外は特に何も設定しません
  • 次に、Lambda関数を作成します
    • python3.10をランタイムとし、適当なコードをテンプレート内に埋め込んでいます (簡単のため)
    • また、サンプルとして、環境変数経由でテンプレート内のパラメータを渡すコードも書いていますが、特に意味はありません
  • 次に、Lambda関数がS3から呼び出される (invokeされる) ことを許可するように、Lambda::Permissionを作成します
    • Lambda関数が呼び出されない場合は、大体この設定を忘れていることが多いと思います
    • 注意点
      • SourceArn としてS3バケットのarnを指定しています。が、ここで !Ref を使うと後で作成するS3バケットと循環参照になってしまうので、バケット名からarnの文字列を作成しています
      • これがないとデプロイ時にエラーが出ます
  • 最後に、S3バケットを作成します
    • NotificationConfiguration -> LambdaConfigurations を指定して、オブジェクトが作られたときに先ほど作成したLambda関数を呼び出すように設定しています
    • 注意点
      • S3バケットの設定をするためには、事前にLambda関数が作られている必要があります。そのため、DependsOn: LambdaFunction を追加しています
      • これがないとデプロイ時にエラーが出ます

作成するリソース

  • Lambda関連
    • AWS::IAM::Role
    • AWS::Lambda::Function
    • AWS::Lambda::Permission
  • S3バケット関連
    • AWS::S3::Bucket

デプロイ方法

必要な準備

デプロイコマンド

Region=ap-northeast-1
OrganizationName=iwatake2222
SystemName=sample-06-a

aws cloudformation deploy \
--region "${Region}" \
--stack-name "${SystemName}"-lambda-s3 \
--template-file ./lambda-s3.yaml \
--capabilities CAPABILITY_NAMED_IAM \
--parameter-overrides \
OrganizationName="${OrganizationName}" \
SystemName="${SystemName}"

動作確認

  • 下記コマンドによって、適当なファイルをS3バケットへアップロードします
touch dummy
aws s3 cp dummy s3://"${OrganizationName}-${SystemName}-bucket"
  • その後、Lambdaのログを確認します。場所は以下の通りです
    • AWS Console -> CloudWatch -> Log Group -> /aws/lambda/${SystemName}
    • (ロググループの下に関数が存在しない場合は、正しいリージョンにいるかどうかも確認してください)
  • ログを見ると、Pythonでprintした内容が出力されていることが分かります
  • また、引数であるevent内に、アップロードされたオブジェクトのバケット名やキー(ファイル名と場所)といった情報が格納されていることが分かります

image.png
image.png

テンプレート

lambda-s3.yaml

lambda-s3.yaml
AWSTemplateFormatVersion: 2010-09-09
Description: |
  Create a lambda function triggered by S3 upload

Parameters:
  OrganizationName:
    Description: Organization Name
    Type: String
  SystemName:
    Description: System Name
    Type: String

Resources:
  #-----------------------------------------------------------------------------
  # Lambda Function
  #-----------------------------------------------------------------------------
  LambdaRole:
    Type: AWS::IAM::Role
    Properties:
      RoleName: !Sub ${SystemName}-lambda-role
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Principal:
              Service:
              - lambda.amazonaws.com
            Action:
              - sts:AssumeRole
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole

  LambdaFunction:
    Type: AWS::Lambda::Function
    Properties:
      FunctionName: !Sub ${SystemName}-lambda
      Runtime: python3.10
      Handler: index.handler
      Role: !GetAtt LambdaRole.Arn
      Environment:
        Variables:
            VarLambdaRole: !Ref LambdaRole
            VarSystemName: !Sub ${SystemName}
      Code:
        ZipFile: |
          import os
          def handler(event, context):
            print('Lambda is called!!')
            print('event: ', event)
            print(os.environ['VarLambdaRole'])
            print(os.environ['VarSystemName'])

  LambdaPermission:
    Type: AWS::Lambda::Permission
    Properties:
      Action: lambda:InvokeFunction
      FunctionName: !Ref LambdaFunction
      Principal: s3.amazonaws.com
      # SourceArn: !GetAtt S3Bucket.Arn    # Avoid recursive reference
      SourceArn: !Sub arn:aws:s3:::${OrganizationName}-${SystemName}-bucket

  #-----------------------------------------------------------------------------
  # S3 bucket
  #-----------------------------------------------------------------------------
  S3Bucket:
    Type: AWS::S3::Bucket
    DependsOn: LambdaFunction
    Properties:
      BucketName: !Sub ${OrganizationName}-${SystemName}-bucket
      NotificationConfiguration:
        LambdaConfigurations:
          - Event: 's3:ObjectCreated:*'
            Function: !GetAtt LambdaFunction.Arn
      Tags:
        - Key: Name
          Value: !Sub ${OrganizationName}-${SystemName}-bucket

S3へのアップロードをトリガーとして、EventBridge経由で呼び出す

構築するアーキテクチャ

image.png

  • 先ほどは、S3から直接Lambdaを呼び出しました
    • しかし、この方法だとS3バケット作成時にLambdaが事前に存在している必要があり、後から追加したりすることができません
    • また、本当に呼び出しを行ったかどうか、どれくらいの頻度で呼び出しが発生したかという情報が分かりません
  • そこで、EventBridgeという仕組みを使います
    • EventBridgeが、S3バケットでのイベントを監視します
    • EventBridgeに対してRuleを設定し、ある条件になったらLambdaを呼び出すようにします
  • S3バケット作成時には、 EventBridgeEnabled: true という設定をするだけで、詳細の設定は不要です
  • 先ほどのテンプレートに加えて、AWS::Events::Rule を作成します
    • この中で、作成したS3バケットへのアップロード(ファイル作成)を条件として、Lambdaを呼び出すように設定します
  • また、Lambdaの呼び出し元がS3からEventBridgeに変わったので、LambdaPermissionのprincipalとsourceも変更が必要になります

作成するリソース

  • Lambda関連
    • AWS::IAM::Role
    • AWS::Lambda::Function
    • AWS::Lambda::Permission
  • EventBridge関連
    • AWS::Events::Rule
  • S3バケット関連
    • AWS::S3::Bucket

デプロイ方法

必要な準備

デプロイコマンド

Region=ap-northeast-1
OrganizationName=iwatake2222
SystemName=sample-06-b

aws cloudformation deploy \
--region "${Region}" \
--stack-name "${SystemName}"-lambda-s3-event \
--template-file ./lambda-s3-event.yaml \
--capabilities CAPABILITY_NAMED_IAM \
--parameter-overrides \
OrganizationName="${OrganizationName}" \
SystemName="${SystemName}"

動作確認

  • 下記コマンドによって、適当なファイルをS3バケットへアップロードします
touch dummy
aws s3 cp dummy s3://"${OrganizationName}-${SystemName}-bucket"
  • その後、Lambdaのログを確認します。場所は以下の通りです
    • AWS Console -> CloudWatch -> Log Group -> /aws/lambda/${SystemName}
  • また、作成したEventBridgeのモニタから、イベントが発生したかどうか、Lambdaを呼び出したかどうか、エラーが発生したかどうか、といったことが分かります。場所は以下の通りです
    • AWS Console -> Amazon EventBridge -> Rule -> 作成したルール (sample-06-b-lambda-s3-event-)
    • 反映されるのに少し時間がかかります

image.png

テンプレート

lambda-s3-event.yaml

lambda-s3-event.yaml
AWSTemplateFormatVersion: 2010-09-09
Description: |
  Create a lambda function triggered by S3 upload via EventBridge

Parameters:
  OrganizationName:
    Description: Organization Name
    Type: String
  SystemName:
    Description: System Name
    Type: String

Resources:
  #-----------------------------------------------------------------------------
  # Lambda Function
  #-----------------------------------------------------------------------------
  LambdaRole:
    Type: AWS::IAM::Role
    Properties:
      RoleName: !Sub ${SystemName}-lambda-role
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Principal:
              Service:
              - lambda.amazonaws.com
            Action:
              - sts:AssumeRole
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole

  LambdaFunction:
    Type: AWS::Lambda::Function
    Properties:
      FunctionName: !Sub ${SystemName}-lambda
      Runtime: python3.10
      Handler: index.handler
      Role: !GetAtt LambdaRole.Arn
      Environment:
        Variables:
            VarLambdaRole: !Ref LambdaRole
            VarSystemName: !Sub ${SystemName}
      Code:
        ZipFile: |
          import os
          def handler(event, context):
            print('Lambda is called!!')
            print('event: ', event)
            print(os.environ['VarLambdaRole'])
            print(os.environ['VarSystemName'])

  LambdaPermission:
    Type: AWS::Lambda::Permission
    Properties:
      Action: lambda:InvokeFunction
      FunctionName: !Ref LambdaFunction
      Principal: events.amazonaws.com
      SourceArn: !GetAtt S3LambdaEventsRule.Arn

  #-----------------------------------------------------------------------------
  # EventBridge
  #-----------------------------------------------------------------------------
  S3LambdaEventsRule:
    Type: AWS::Events::Rule
    Properties:
      State: ENABLED
      EventPattern:
        source:
          - aws.s3
        detail:
          reason:
            - CopyObject
            - PutObject
            - CompleteMultipartUpload
          bucket:
            name:
              - !Ref S3Bucket
          # object:
          #   key:
          #     - suffix: .jpg
      Targets:
        - Arn: !GetAtt LambdaFunction.Arn
          Id: LambdaFunctionTarget

  #-----------------------------------------------------------------------------
  # S3 bucket
  #-----------------------------------------------------------------------------
  S3Bucket:
    Type: AWS::S3::Bucket
    Properties:
      BucketName: !Sub ${OrganizationName}-${SystemName}-bucket
      NotificationConfiguration:
        EventBridgeConfiguration:
          EventBridgeEnabled: true
      Tags:
        - Key: Name
          Value: !Sub ${OrganizationName}-${SystemName}-bucket

S3へのアップロードをトリガーとして、SQS経由で呼び出す

構築するアーキテクチャ

image.png

  • 今度は、EventBridgeの代わりにSimple Queue Service (SQS) を使ってLambdaを呼び出してみます
  • S3バケットにファイルがアップロードされたら、キューに情報を送信するようにします
    • そのために、S3バケット作成時に NotificationConfiguration -> QueueConfigurations で後で作成するSQSを指定します
      • ちなみに、ここの結合を疎にするため、S3 -> EventBridge -> SQS -> Lambdaとすることも出来ます。参考
  • SQSキューを作成します
    • 色々と設定はあるのですが、ここではデフォルトのままにします
    • S3からこのキューに送信できるように、QueuePolicy の設定をします。これを忘れるとデプロイ時にS3バケット作成でエラーが出ます
  • Lambda関数の方は、トリガーがSQSに変わったため、Lambda:Permission のsourceとprincipalを変更しておきます
  • また、SQSにメッセージが送られたことをトリガーとしてLambdaを呼び出すように EventSourceMapping の設定をします
  • Lambda関数のコード内で、わざわざキューから receive_message をしたりということは不要です。これまでと同じように呼び出し時の引数 event 内に必要な情報は格納されています。また、キューからメッセージの削除も自動的に行われます
    • ただし、明示的にSQSの操作をしないでも、上記の通り実際にLambdaとしてSQSに対して操作をするのでLambdaのIAMロールに対して、SQSに対するアクセス権限を付与するポリシーの設定が必要になります

作成するリソース

  • Lambda関連
    • AWS::IAM::Role
    • AWS::Lambda::Function
    • AWS::Lambda::Permission
    • AWS::Lambda::EventSourceMapping
  • SQS関連
    • AWS::SQS::Queue
    • AWS::SQS::QueuePolicy
  • S3バケット関連
    • AWS::S3::Bucket

デプロイ方法

必要な準備

デプロイコマンド

Region=ap-northeast-1
OrganizationName=iwatake2222
SystemName=sample-06-c

aws cloudformation deploy \
--region "${Region}" \
--stack-name "${SystemName}"-lambda-s3-sqs \
--template-file ./lambda-s3-sqs.yaml \
--capabilities CAPABILITY_NAMED_IAM \
--parameter-overrides \
OrganizationName="${OrganizationName}" \
SystemName="${SystemName}"

動作確認

  • 下記コマンドによって、適当なファイルをS3バケットへアップロードします
touch dummy
aws s3 cp dummy s3://"${OrganizationName}-${SystemName}-bucket"
  • その後、Lambdaのログを確認します。場所は以下の通りです
    • AWS Console -> CloudWatch -> Log Group -> /aws/lambda/${SystemName}
  • また、作成したSQSのモニタリングから、イベントが発生したかどうか、Lambdaを呼び出したかどうか、エラーが発生したかどうか、といったことが分かります。場所は以下の通りです
    • AWS Console -> Amazon SQS -> キュー -> 作成したキュー (sample-06-c-queue)
    • また、「メッセージを送受信」からメッセージを送信することでもLambdaを呼び出すことが可能です
      • S3へファイルをアップロードしたら受信メッセージが現れるはずですが、ポーリングするよりも前にLambdaが呼ばれてメッセージは即削除されて見えないようです
      • (トリガーを一時的に外せば確認できます)

テンプレート

lambda-s3-sqs.yaml

lambda-s3-sqs.yaml
AWSTemplateFormatVersion: 2010-09-09
Description: |
  Create a lambda function triggered by S3 upload via SQS Queue

Parameters:
  OrganizationName:
    Description: Organization Name
    Type: String
  SystemName:
    Description: System Name
    Type: String

Resources:
  #-----------------------------------------------------------------------------
  # Lambda Function
  #-----------------------------------------------------------------------------
  LambdaRole:
    Type: AWS::IAM::Role
    Properties:
      RoleName: !Sub ${SystemName}-lambda-role
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Principal:
              Service:
              - lambda.amazonaws.com
            Action:
              - sts:AssumeRole
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
      Policies:
        - PolicyName: !Sub ${SystemName}-lambda-policy
          PolicyDocument:
            Version: 2012-10-17
            Statement:
              - Effect: Allow
                Action:
                  - 'sqs:*'
                Resource:
                  - !GetAtt SQSQueue.Arn

  LambdaFunction:
    Type: AWS::Lambda::Function
    Properties:
      FunctionName: !Sub ${SystemName}-lambda
      Runtime: python3.10
      Handler: index.handler
      Role: !GetAtt LambdaRole.Arn
      Environment:
        Variables:
            SQSQueueUrl: !GetAtt SQSQueue.QueueUrl
      Code:
        ZipFile: |
          import boto3
          import os
          def handler(event, context):
            print('Lambda is called!!')
            print('event: ', event)

            # # No need to receive_message
            # queue_url = os.environ['SQSQueueUrl']
            # sqs_client = boto3.client('sqs')
            # response = sqs_client.receive_message(QueueUrl=queue_url)
            # print(response)

  LambdaPermission:
    Type: AWS::Lambda::Permission
    Properties:
      Action: lambda:InvokeFunction
      FunctionName: !Ref LambdaFunction
      Principal: sqs.amazonaws.com
      SourceArn: !GetAtt SQSQueue.Arn

  LambdaEventSourceMapping:
    Type: AWS::Lambda::EventSourceMapping
    Properties:
      EventSourceArn: !GetAtt SQSQueue.Arn
      FunctionName: !GetAtt LambdaFunction.Arn

  #-----------------------------------------------------------------------------
  # SQS Queue
  #-----------------------------------------------------------------------------
  SQSQueue:
    Type: AWS::SQS::Queue
    Properties:
      QueueName: !Sub ${SystemName}-queue
      # ContentBasedDeduplication: true
      # FifoQueue: true

  SQSQueuePolicy:
    Type: AWS::SQS::QueuePolicy
    Properties:
      PolicyDocument:
        Version: 2012-10-17
        Statement:
          - Action:
            - sqs:SendMessage
            Effect: Allow
            Principal: '*'
            Resource: !GetAtt SQSQueue.Arn
            Condition:
              ArnEquals:
                # aws:SourceArn: !GetAtt S3Bucket.Arn    # Avoid recursive reference
                aws:SourceArn: !Sub arn:aws:s3:::${OrganizationName}-${SystemName}-bucket
      Queues:
        - !Ref SQSQueue

  #-----------------------------------------------------------------------------
  # S3 bucket
  #-----------------------------------------------------------------------------
  S3Bucket:
    Type: AWS::S3::Bucket
    DependsOn: SQSQueue
    Properties:
      BucketName: !Sub ${OrganizationName}-${SystemName}-bucket
      NotificationConfiguration:
        QueueConfigurations:
          - Event: 's3:ObjectCreated:*'
            Queue: !GetAtt SQSQueue.Arn
      Tags:
        - Key: Name
          Value: !Sub ${OrganizationName}-${SystemName}-bucket
2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?