S3イベントをトリガーにLambda関数を実行

前回、ローカル環境でS3のputイベントに対してLambdaを実行するテストを行いました。

詳細については、こちらの記事をご覧ください：

https://qiita.com/kutinasi_hobby/items/6e76d640598a10e89d14

今回は、AWS環境で実際にこれを実行する方法について説明します。

## CloudFormationを使用してリソースの作成
AWS SAM（Serverless Application Model）はCloudFormationを使用してリソースの作成を行います。SAMプロジェクト内のtemplate.yamlファイルにリソース情報を記述することで、必要なリソースを簡単に定義できます。

ここでは、ファイルをアップロードするS3バケット、処理を行うLambda関数、そしてそのLambda関数に適切な権限を付与するためのIAMロールを作成します。

```yaml:template.yaml
AWSTemplateFormatVersion: '2010-09-09'  # テンプレートのバージョンを指定
Transform: 'AWS::Serverless-2016-10-31'  # AWS SAM を使用するための変換ルール

Resources:
  MyBucket:
    Type: 'AWS::S3::Bucket'  # S3 バケットのリソースタイプを指定
      
  S3EventProcessorFunction:
    Type: 'AWS::Serverless::Function'  # サーバーレスの Lambda 関数を指定
    Properties:
      Handler: app.lambda_handler  # Lambda 関数のハンドラ名を指定
      Runtime: python3.11  # Lambda 関数の実行ランタイムを指定
      CodeUri: hello_world/  # Lambda 関数のコードが配置されているディレクトリを指定
      Events:
        S3Event:
          Type: S3  # S3 イベントトリガーを指定
          Properties:
            Bucket: !Ref MyBucket  # トリガーする S3 バケットを参照
            Events: s3:ObjectCreated:*  # オブジェクトが作成されたときにトリガーするイベントを指定

  LambdaExecutionRole:
    Type: 'AWS::IAM::Role'  # IAM ロールのリソースタイプを指定
    Properties:
      AssumeRolePolicyDocument:  # ロールを引き受けるためのポリシードキュメント
        Version: '2012-10-17'
        Statement:
          - Effect: 'Allow'  # 許可を指定
            Principal:
              Service: 'lambda.amazonaws.com'  # Lambda サービスを指定
            Action: 'sts:AssumeRole'  # ロールの引き受けを許可するアクション
      Policies:
        - PolicyName: 'S3AccessPolicy'  # S3 へのアクセスを許可するポリシー名
          PolicyDocument:
            Version: '2012-10-17'
            Statement:
              - Effect: 'Allow'  # 許可を指定
                Action:
                  - 's3:PutObject'  # S3 バケットにオブジェクトを置くアクション
                  - 's3:GetObject'  # S3 バケットからオブジェクトを取得するアクション
                Resource: !Sub 'arn:aws:s3:::${MyBucket}/*'  # 特定の S3 バケットリソースを指定
        - PolicyName: 'CloudWatchLogsPolicy'  # CloudWatch Logs へのアクセスを許可するポリシー名
          PolicyDocument:
            Version: '2012-10-17'
            Statement:
              - Effect: 'Allow'  # 許可を指定
                Action:
                  - 'logs:CreateLogGroup'  # ロググループの作成を許可
                  - 'logs:CreateLogStream'  # ログストリームの作成を許可
                  - 'logs:PutLogEvents'  # ログイベントのプットを許可
                Resource: 'arn:aws:logs:*:*:*'  # 全てのログリソースを指定
```
↓修正: Roleを無駄に設定していたため削除
```yaml:template.yaml
AWSTemplateFormatVersion: '2010-09-09'  # テンプレートのバージョンを指定
Transform: 'AWS::Serverless-2016-10-31'  # AWS SAM を使用するための変換ルール

Resources:
  MyBucket:
    Type: 'AWS::S3::Bucket'  # S3 バケットのリソースタイプを指定
      
  S3EventProcessorFunction:
    Type: 'AWS::Serverless::Function'  # サーバーレスの Lambda 関数を指定
    Properties:
      Handler: app.lambda_handler  # Lambda 関数のハンドラ名を指定
      Runtime: python3.11  # Lambda 関数の実行ランタイムを指定
      CodeUri: hello_world/  # Lambda 関数のコードが配置されているディレクトリを指定
      Events:
        S3Event:
          Type: S3  # S3 イベントトリガーを指定
          Properties:
            Bucket: !Ref MyBucket  # トリガーする S3 バケットを参照
            Events: s3:ObjectCreated:*  # オブジェクトが作成されたときにトリガーするイベントを指定

```

## Build の実行
AWS SAMを使用してサーバーレスアプリケーションをAWSにデプロイする際に、sam deploy --guidedコマンドは非常に便利です。このコマンドを実行すると、対話型でデプロイの設定を簡単に行うことができます。
```ターミナル
> sam deploy --guided                                      
        
Configuring SAM deploy
======================

        Looking for config file [samconfig.toml] :  Found
        Reading default arguments  :  Success

        Setting default arguments for 'sam deploy'
        =========================================
        Stack Name [stack-sam-sample]: S3LambdaTriggerStack
        AWS Region [ap-northeast-1]:
        #Shows you resources changes to be deployed and require a 'Y' to initiate deploy
        Confirm changes before deploy [Y/n]: Y
        #SAM needs permission to be able to create roles to connect to the resources in your template
        Allow SAM CLI IAM role creation [Y/n]: n
        Capabilities [['CAPABILITY_IAM']]:
        #Preserves the state of previously provisioned resources when an operation fails
        Disable rollback [Y/n]: n
        HelloWorldFunction has no authentication. Is this okay? [y/N]: y
        Save arguments to configuration file [Y/n]: n

        Looking for resources needed for deployment:
        Creating the required resources...
        ...
        Deploy this changeset? [y/N]: y
Successfully created/updated stack - S3LambdaTriggerStack in ap-northeast-1
```
Stack Name: S3LambdaTriggerStack - デプロイするCloudFormationスタックの名

AWS Region: ap-northeast-1 - デプロイ先リージョン

Confirm changes before deploy: Y - CloudFormationが行うリソース変更確認

Allow SAM CLI IAM role creation: n - SAM CLIによるIAMロールの自動作成

Capabilities: ['CAPABILITY_IAM'] - CloudFormationのIAMリソース操作許可

Disable rollback: n - デプロイ失敗時にCloudFormationによるロールバック

HelloWorldFunction has no authentication. Is this okay?: y - Lambda関数の認証

Save arguments to configuration file: n - この設定を設定ファイルへ保存

Deploy this changeset? [y/N]: y - CloudFormationで変更セットを実際にデプロイ

## CloudFormation の確認
Build時に指定したスタック名とSAM CLIが使用するデフォルトのスタックが作成されています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3517045/b941cc2e-7df8-a69d-afb8-a8e01906b04a.png)

## リソースの確認
### S3
AWS CloudFormationを使用して、デプロイしたスタックには通常、defaultという名前のバケットが含まれています。これは、スタックが使用するためのデフォルトのバケットです。

また、ファイルをアップロードするために使用する別のバケットとして、mybucketという名前を含むバケットが作成されました。このバケットにファイルをアップロードしてLambdaの動作確認を行います。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3517045/3d70dd7c-9f38-e0b0-656c-8aa435c92fff.png)

## S3バケットにファイルをアップロード
mybucket と入っている方のバケットに適当なファイルをアプロードします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3517045/d7b3dc92-aafe-b5d3-acfe-26f7039e5a85.png)

## Lambda実行の確認
S3にファイルがアップロードされるとLambda関数が自動的に実行されます。
このLambda関数は、アップロードされたファイル名とそのファイルが保存されているバケット名をCloudWatchに出力します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3517045/e246b6ba-3e6f-3a3b-f194-6a77e7933aa6.png)

画像の中で日本語が文字化けしていますが、Lambda関数は正しく実行され、期待通りの情報を出力していることが確認できました。

Lambda関数のコードをカスタマイズすることで、さまざまな事に応用できそうです。

## リソースの削除
AWS CloudFormationを使用して、リソースを管理しているので、スタックの削除が完了すればリソースがすべて解放されます。しかし、実際にはS3バケットが空でない場合、スタックの削除操作が失敗することがあります。
スタックの削除前にS3バケットの中身を空にしておきましょう。