AWS SAMを使って最もシンプルにLambda × S3 のS3イベント駆動プログラムを構築する

# 概要

今回は以前から紹介しているAWS LambdaのAWS サーバーレスアプリケーションモデル (Serverless Application Model)、AWS SAMの続編になります。

【以前の記事】
導入編：[AWS Lambda SAMとは？～Cloud9でSAM Sampleを使ってPythonのLambdaプログラムを簡単にデプロイする～](https://www.studycloudtech.com/aws/lambda-sam-start-cloud9/ "AWS Lambda SAMとは？～Cloud9でSAM Sampleを使ってPythonのLambdaプログラムを簡単にデプロイする～")
第１回：[AWS SAMを使って最もシンプルにLambda × APIGatewayのWebAPIを構築する](https://qiita.com/fkooo/items/e08cdea0f73f60dabd3d "AWS SAMを使って最もシンプルにLambda × APIGatewayのWebAPIを構築する")
第２回：[AWS SAMを使って最もシンプルにLambda × S3 のS3イベント駆動プログラムを構築する](https://qiita.com/fkooo/items/fa1d03c1d9e532956e2d "AWS SAMを使って最もシンプルにLambda × S3 のS3イベント駆動プログラムを構築する")（本記事）

第１回は、Lambda × APIGateway 編にしたので、続編のLambda × S3 編です。
この投稿を最後まで実行すると以下の図のようなAWSサービスで構築したS3のイベントによって処理が動き出すS3イベント駆動プログラムが、なんと**2ファイル50ライン**ほどで完成します。
理解しやすいように**最もシンプルな記載例**としてファイルサンプルを作成したので実際に作ったファイルを見るとこんなに簡単なのかを思えると思います。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/188544/0f43a8c5-c542-eea3-2029-1a2887e37d45.png)


## 想定読者
* Lambda × S3 でS3イベント駆動プログラムを作りたい人
* LambdaでSAMを使ってみたい人
* Lambdaの知識をレベルアップしたい人

## 作業時間

* 15分（Cloud9などSAMの環境作成除く(^^;)）

## 参考書籍

[AWS Lambda実践ガイド 第2版](https://amzn.to/3E5O6xm "AWS Lambda実践ガイド 第2版")
<a href="https://www.amazon.co.jp/AWS-Lambda%E5%AE%9F%E8%B7%B5%E3%82%AC%E3%82%A4%E3%83%89-%E7%AC%AC2%E7%89%88-impress-gear/dp/4295013307?__mk_ja_JP=%E3%82%AB%E3%82%BF%E3%82%AB%E3%83%8A&crid=RTIKKZ2CFWRN&keywords=lambda&qid=1649573956&sprefix=lambda%2Caps%2C259&sr=8-1-spons&psc=1&spLa=ZW5jcnlwdGVkUXVhbGlmaWVyPUEyRlJEWjJYMjVSTlkyJmVuY3J5cHRlZElkPUEwMDQ2MDAxMkxOTDlZODNUMDJKVSZlbmNyeXB0ZWRBZElkPUFZUFVHWkVYQkwxRUkmd2lkZ2V0TmFtZT1zcF9hdGYmYWN0aW9uPWNsaWNrUmVkaXJlY3QmZG9Ob3RMb2dDbGljaz10cnVl&linkCode=li1&tag=aws_lambda_002-22&linkId=642f50ab03838b3aabea490e9486a91c&language=ja_JP&ref_=as_li_ss_il" target="_blank"><img border="0" src="//ws-fe.amazon-adsystem.com/widgets/q?_encoding=UTF8&ASIN=4295013307&Format=_SL110_&ID=AsinImage&MarketPlace=JP&ServiceVersion=20070822&WS=1&tag=aws_lambda_002-22&language=ja_JP" ></a><img src="https://ir-jp.amazon-adsystem.com/e/ir?t=aws_lambda_002-22&language=ja_JP&l=li1&o=9&a=4295013307" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;" />

[【Kindle版】AWS Lambda実践ガイド 第2版](https://amzn.to/37vUsd1 "【Kindle版】AWS Lambda実践ガイド 第2版")
<a href="https://www.amazon.co.jp/AWS-Lambda%E5%AE%9F%E8%B7%B5%E3%82%AC%E3%82%A4%E3%83%89-%E7%AC%AC2%E7%89%88-impress-gear%E3%82%B7%E3%83%AA%E3%83%BC%E3%82%BA-ebook/dp/B09TVG5H17?_encoding=UTF8&qid=1649573956&sr=8-1-spons&linkCode=li1&tag=aws_lambda_002-22&linkId=51d23857df9fcbf5549fcd41fdf628da&language=ja_JP&ref_=as_li_ss_il" target="_blank"><img border="0" src="//ws-fe.amazon-adsystem.com/widgets/q?_encoding=UTF8&ASIN=B09TVG5H17&Format=_SL110_&ID=AsinImage&MarketPlace=JP&ServiceVersion=20070822&WS=1&tag=aws_lambda_002-22&language=ja_JP" ></a><img src="https://ir-jp.amazon-adsystem.com/e/ir?t=aws_lambda_002-22&language=ja_JP&l=li1&o=9&a=B09TVG5H17" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;" />

ほぼほぼこの本を参考にさせて頂きました。
Lambdaに特化した本はあまりないので現時点で唯一の専門書と思います。
Lambdaを勉強するなら誰でもまずこの本から勉強するのではないかと思える代表的な本になっていると思います。

Lambdaの良書が増えてきたので、おすすめ本5冊を以下で紹介しています。Lambdaについて本格的に勉強したい方はぜひ読んでみてください。

[AWS Lambdaおすすめ書籍5冊の紹介](https://www.studycloudtech.com/aws/aws-lambda-books/ "AWS Lambdaおすすめ書籍5冊の紹介")

## 目次

* SAM環境（Cloud9環境）準備
* Lambda × S3 テンプレートファイル作成
* Lambda ソースファイル作成
* ビルド
* デプロイ
* 動作確認

## SAM環境（Cloud9環境）準備

導入編の以下の記事を参考にCloud9の環境を作ってください。
**ディスク拡張を忘れず(^^;)**
[AWS Lambda SAMとは？～Cloud9でSAM Sampleを使ってPythonのLambdaプログラムを簡単にデプロイする～](https://www.studycloudtech.com/aws/lambda-sam-start-cloud9/ "AWS Lambda SAMとは？～Cloud9でSAM Sampleを使ってPythonのLambdaプログラムを簡単にデプロイする～")

## Lambda × S3 テンプレートファイル作成

Cloud9環境が作成されたら実際にAWSサービス構築用のファイルを作成していきます。
ファイルの中身が違うだけでほぼ第１回と同じ内容になります。
作成するファイルは2つ(**template.yaml**/**app.py**)。最終的には以下のフォルダ構成になるように2ファイル作成してください。

```
sam-s3input-sample
│  template.yaml
└─hello_world
　    app.py
```

ルートフォルダは**sam-s3input-sample**という名前にしています。こちらのフォルダ名は任意なので必要であれば変えてください。他のフォルダ名／ファイル名を変更する場合、それに伴った設定を変える必要があるので少し慣れてきてからにした方が良いと思います(^^;)



それではまず**template.yaml**で**Lambda × S3 テンプレート**ファイル作成します。
ファイル内容は以下の通り。

```template.yaml
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: >
  Sample SAM Template for sam-s3input-sample

Parameters:
  BucketName:
    Type: String
    Default: "sam-s3input-sample-v1"

Globals:
  Function:
    Timeout: 3

Resources:
  # S3
  S3InputBucket:
    Type: AWS::S3::Bucket
    Properties:
      BucketName: !Ref BucketName

  # Lambda
  HelloWorldFunction:
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: hello_world/
      Handler: app.lambda_handler
      Runtime: python3.9
      Architectures:
        - x86_64
      Events:
        S3UploadEvent:
          Type: S3
          Properties:
            Bucket: !Ref S3InputBucket
            Events: s3:ObjectCreated:*

Outputs:
  S3InputBucketArn:
    Description: "Hello World S3 ARN"
    Value: !GetAtt S3InputBucket.Arn
  HelloWorldFunction:
    Description: "Hello World Lambda Function ARN"
    Value: !GetAtt HelloWorldFunction.Arn
  HelloWorldFunctionIamRole:
    Description: "Implicit IAM Role created for Hello World function"
    Value: !GetAtt HelloWorldFunctionRole.Arn
```

記載内容を極力シンプルにしたので定義内容もなんとなくわかると思いますが、
抜粋で記載内容を説明すると、、、
* **Parameters -> BucketName -> Default: "sam-s3input-sample-v1"（作成するS3のバケット名です）**
* Timeout: 3（Lambdaの処理タイムアウト3秒）
* CodeUri: hello_world/（Lambdaの実ファイルはhello_worldフォルダに配置します）
* Handler: app.lambda_handler（app.pyファイルのlambda_handler関数が呼ばれます）
* Events: s3:ObjectCreated:*（S3にフォルダやファイルのオブジェクトが作成されるイベントが発生したら動作する設定になっています）
ことを定義しています。

地味に重要なのが一番上の Default: **"sam-s3input-sample-v1"** になります。
S3はリージョン内で一意のバケット名にする必要があるので、初回 **"sam-s3input-sample-v1"** で作成した場合、同じテンプレートから新しいアプリケーションを作ろうとしても出来ません。その時、Default のS3バケット名 **"sam-s3input-sample-v1"** から変更する必要があります。（あとで出てきますが、このテンプレートファイルを変更しなくても、デプロイの時にS3バケット名の変更可能です）

ちなみにS3バケット名の存在確認は以下のコマンドで事前確認可能です。
**--bucket** 以降に確認したいバケット名を指定します。

```sh
$ aws s3api head-bucket --bucket s3-sample
An error occurred (403) when calling the HeadBucket operation: Forbidden
```
**403 Forbidden** のため既に誰かが使用しています。この名前ではS3バケットは **作成不可** になります。


```sh
$ aws s3api head-bucket --bucket sam-s3input-sample-v1
An error occurred (404) when calling the HeadBucket operation: Not Found
```

**404 Not Found** のため誰も使用していません。この名前で新しいS3バケットが **作成可能** です。

```sh
$ aws s3api head-bucket --bucket sam-s3input-sample-v1
$ 
```

自身のアカウント内で作成しているとコマンド実行結果はこんな感じです。（戻り値無しです。今回の投稿内容を最後までビルドした後、コマンド実行した結果になります）
この場合も自分で既に使っているバケット名になるため、 **作成不可** になります。

最後に**Outputs**以降は実際に作成されたS3やLambdaのARNを出力するようにしていますが、実際にはなくても構いません。そのため、実際に構築に必要な設定は、**Outputs**の上までなので、なんとたったの**36ライン**でLambda × S3環境が出来上がります！簡単ですね。

## Lambda ソースファイル作成

では、次のファイル**app.py**に移ります。
**app.py** は、実際にS3にファイルが作成されたときに実行されるLambda関数になります。
今回は単純なサンプルということで処理の中身はなく、S3のオブジェクト作成のEvent内容をログ出力するだけの処理にしています。
たったの**5ライン**！
上で紹介したフォルダ構成で**hello_world**フォルダの下に配置しましょう。

```app.py
import json

def lambda_handler(event, context):
    print('event:' + json.dumps(event))
    return ""
```

最終的にCloud9環境で見えるフォルダ構成が以下のようになっていれば問題ありません。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/188544/b27e29e3-191d-f1ad-00a4-6d308f0ff3ca.png)

## ビルド

準備はここまでです。たった**2フォルダ／2ファイル**で非常に簡単ですね。

それではビルドします。
ルートフォルダ**sam-s3input-sample**に移動して、
　**sam build --use-container**
実行しましょう。

このあたりのコマンドは以前の記事に記載しているので必要であれば以下の記事を参照してください。
【以前の記事】
[AWS Lambda SAMとは？～Cloud9でSAM Sampleを使ってPythonのLambdaプログラムを簡単にデプロイする～](https://www.studycloudtech.com/aws/lambda-sam-start-cloud9/ "AWS Lambda SAMとは？～Cloud9でSAM Sampleを使ってPythonのLambdaプログラムを簡単にデプロイする～")

以下に実際のコマンド実行結果を載せています。
```sh
$ cd sam-s3input-sample
$ sam build --use-container
Starting Build inside a container
Building codeuri: /home/ec2-user/environment/Cloud9_SAM/sam-s3input-sample/hello_world runtime: python3.9 metadata: {} architecture: x86_64 functions: HelloWorldFunction

Fetching public.ecr.aws/sam/build-python3.9:latest-x86_64 Docker container image...................................................................................................................................................................................................................................................................................................................................................................
Mounting /home/ec2-user/environment/Cloud9_SAM/sam-s3input-sample/hello_world as /tmp/samcli/source:ro,delegated inside runtime container

Build Succeeded

Built Artifacts  : .aws-sam/build
Built Template   : .aws-sam/build/template.yaml

Commands you can use next
=========================
[*] Validate SAM template: sam validate
[*] Invoke Function: sam local invoke
[*] Test Function in the Cloud: sam sync --stack-name {stack-name} --watch
[*] Deploy: sam deploy --guided
        
requirements.txt file not found. Continuing the build without dependencies.
Running PythonPipBuilder:CopySource
$ 
```

ディスク容量が問題なければ無事にビルドできると思います(^^;)
くどいようですが、ディスク容量でエラー（**RuntimeError: Container does not exist. Cannot get logs for this container**）が出たら以前の記事を参照してください。

【以前の記事】
[AWS Lambda SAMとは？～Cloud9でSAM Sampleを使ってPythonのLambdaプログラムを簡単にデプロイする～](https://www.studycloudtech.com/aws/lambda-sam-start-cloud9/ "AWS Lambda SAMとは？～Cloud9でSAM Sampleを使ってPythonのLambdaプログラムを簡単にデプロイする～")

## デプロイ

続いてデプロイです。
　**sam deploy --guided --capabilities CAPABILITY_IAM CAPABILITY_AUTO_EXPAND**
実行しましょう。

```sh
$ sam deploy --guided --capabilities CAPABILITY_IAM CAPABILITY_AUTO_EXPAND

Configuring SAM deploy
======================

        Looking for config file [samconfig.toml] :  Not found

        Setting default arguments for 'sam deploy'
        =========================================
        Stack Name [sam-app]: sam-s3input-sample
        AWS Region [ap-northeast-1]: 
        Parameter BucketName [sam-s3input-sample-v1]: 
        #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]: y
        #Preserves the state of previously provisioned resources when an operation fails
        Disable rollback [y/N]: y
        Save arguments to configuration file [Y/n]: y
        SAM configuration file [samconfig.toml]: 
        SAM configuration environment [default]: 

        Looking for resources needed for deployment:
         Managed S3 bucket: aws-sam-cli-managed-default-samclisourcebucket-bnxxxxxv3w3n
         A different default S3 bucket can be set in samconfig.toml

        Saved arguments to config file
        Running 'sam deploy' for future deployments will use the parameters saved above.
        The above parameters can be changed by modifying samconfig.toml
        Learn more about samconfig.toml syntax at 
        https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-config.html

Uploading to sam-s3input-sample/f046ce4caxxxxxXXXXXXX2172a4585fd2a  195 / 195  (100.00%)

        Deploying with following values
        ===============================
        Stack name                   : sam-s3input-sample
        Region                       : ap-northeast-1
        Confirm changeset            : True
        Disable rollback             : True
        Deployment s3 bucket         : aws-sam-cli-managed-default-samclisourcebucket-bnxxxxxv3w3n
        Capabilities                 : ["CAPABILITY_IAM", "CAPABILITY_AUTO_EXPAND"]
        Parameter overrides          : {"BucketName": "sam-s3input-sample-v1"}
        Signing Profiles             : {}

Initiating deployment
=====================
Uploading to sam-s3input-sample/ce4caxxxxxXXXXXXX2172a4585fd2a.template  1375 / 1375  (100.00%)

Waiting for changeset to be created..
CloudFormation stack changeset
-------------------------------------------------------------------------------------------------------------------------------------------------------------
Operation                               LogicalResourceId                       ResourceType                            Replacement                           
-------------------------------------------------------------------------------------------------------------------------------------------------------------
+ Add                                   HelloWorldFunctionRole                  AWS::IAM::Role                          N/A                                   
+ Add                                   HelloWorldFunctionS3UploadEventPermis   AWS::Lambda::Permission                 N/A                                   
                                        sion                                                                                                                  
+ Add                                   HelloWorldFunction                      AWS::Lambda::Function                   N/A                                   
+ Add                                   S3InputBucket                           AWS::S3::Bucket                         N/A                                   
-------------------------------------------------------------------------------------------------------------------------------------------------------------

Changeset created successfully. arn:aws:cloudformation:ap-northeast-1:XXXXXXXXXXXX:changeSet/samcli-deploy1658624428/xxxxa72d-xxxx-xxxx-8e82-b24e8d7exxxx


Previewing CloudFormation changeset before deployment
======================================================
Deploy this changeset? [y/N]: y

2022-07-24 01:00:43 - Waiting for stack create/update to complete

CloudFormation events from stack operations (refresh every 0.5 seconds)
-------------------------------------------------------------------------------------------------------------------------------------------------------------
ResourceStatus                          ResourceType                            LogicalResourceId                       ResourceStatusReason                  
-------------------------------------------------------------------------------------------------------------------------------------------------------------
CREATE_IN_PROGRESS                      AWS::IAM::Role                          HelloWorldFunctionRole                  -                                     
CREATE_IN_PROGRESS                      AWS::IAM::Role                          HelloWorldFunctionRole                  Resource creation Initiated           
CREATE_COMPLETE                         AWS::IAM::Role                          HelloWorldFunctionRole                  -                                     
CREATE_IN_PROGRESS                      AWS::Lambda::Function                   HelloWorldFunction                      -                                     
CREATE_IN_PROGRESS                      AWS::Lambda::Function                   HelloWorldFunction                      Resource creation Initiated           
CREATE_COMPLETE                         AWS::Lambda::Function                   HelloWorldFunction                      -                                     
CREATE_IN_PROGRESS                      AWS::Lambda::Permission                 HelloWorldFunctionS3UploadEventPermis   -                                     
                                                                                sion                                                                          
CREATE_IN_PROGRESS                      AWS::Lambda::Permission                 HelloWorldFunctionS3UploadEventPermis   Resource creation Initiated           
                                                                                sion                                                                          
CREATE_COMPLETE                         AWS::Lambda::Permission                 HelloWorldFunctionS3UploadEventPermis   -                                     
                                                                                sion                                                                          
CREATE_IN_PROGRESS                      AWS::S3::Bucket                         S3InputBucket                           -                                     
CREATE_IN_PROGRESS                      AWS::S3::Bucket                         S3InputBucket                           Resource creation Initiated           
CREATE_COMPLETE                         AWS::S3::Bucket                         S3InputBucket                           -                                     
CREATE_COMPLETE                         AWS::CloudFormation::Stack              sam-s3input-sample                      -                                     
-------------------------------------------------------------------------------------------------------------------------------------------------------------
CloudFormation outputs from deployed stack
--------------------------------------------------------------------------------------------------------------------------------------------------------------
Outputs                                                                                                                                                      
--------------------------------------------------------------------------------------------------------------------------------------------------------------
Key                 HelloWorldFunctionIamRole                                                                                                                
Description         Implicit IAM Role created for Hello World function                                                                                       
Value               arn:aws:iam::XXXXXXXXXXXX:role/sam-s3input-sample-HelloWorldFunctionRole-1XXXXXZYF42OS1                                                   

Key                 S3InputBucketArn                                                                                                                         
Description         Hello World S3 ARN                                                                                                                       
Value               arn:aws:s3:::sam-s3input-sample-v1                                                                                                       

Key                 HelloWorldFunction                                                                                                                       
Description         Hello World Lambda Function ARN                                                                                                          
Value               arn:aws:lambda:ap-northeast-1:XXXXXXXXXXXX:function:sam-s3input-sample-HelloWorldFunction-XXXXXPNxFsjj                                   
--------------------------------------------------------------------------------------------------------------------------------------------------------------

Successfully created/updated stack - sam-s3input-sample in ap-northeast-1

$ 
```

対話型のやり取りになるので以下のように求められた内容に対し回答を入力します。
**[]** 内に記載されたデフォルトで良ければそのままEnterで大丈夫です。
前述した **Parameter BucketName** で、**S3バケット名をデフォルトから変更可能**です。
今回のサンプルは指定無し（デフォルトS3バケット名:sam-s3input-sample-v1 を使用しています）

* Stack Name [sam-app]: sam-s3input-sample
* AWS Region [ap-northeast-1]: 
* **Parameter BucketName [sam-s3input-sample-v1]:**
* Confirm changes before deploy [y/N]: **y**
* Allow SAM CLI IAM role creation [Y/n]: **y**
* Disable rollback [y/N]: **y**
* Save arguments to configuration file [Y/n]: **y**
* SAM configuration file [samconfig.toml]: 
* SAM configuration environment [default]: 
* Deploy this changeset? [y/N]: **y**

このやり取りは、**SAM configuration file [samconfig.toml]** に保存されるので、
次回は**sam deploy**というオプション無しの簡単なコマンド実行で大丈夫です。

ちなみにバケット名が既に存在しているためS3バケットが作成できなかった場合、
以下のエラーになります。

```sh
sam-s3input-sample-v1 already exists
The following resource(s) failed to
create: [S3InputBucket].
```
と、表示されるので比較的わかりやすいエラーメッセージになります(^^)
全量は以下の通りです。

```sh
$ sam deploy --guided --capabilities CAPABILITY_IAM CAPABILITY_AUTO_EXPAND

Configuring SAM deploy
======================

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

        Setting default arguments for 'sam deploy'
        =========================================
        Stack Name [sam-s3input-sample]: 
        AWS Region [ap-northeast-1]: 
        Parameter BucketName [sam-s3input-sample-v1]: 
        #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]: y
        #Preserves the state of previously provisioned resources when an operation fails
        Disable rollback [Y/n]: y
        Save arguments to configuration file [Y/n]: y
        SAM configuration file [samconfig.toml]: 
        SAM configuration environment [default]: 

        Looking for resources needed for deployment:
         Managed S3 bucket: aws-sam-cli-managed-default-samclisourcebucket-bncdmvrv3w3n
         A different default S3 bucket can be set in samconfig.toml

        Saved arguments to config file
        Running 'sam deploy' for future deployments will use the parameters saved above.
        The above parameters can be changed by modifying samconfig.toml
        Learn more about samconfig.toml syntax at 
        https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-config.html

Uploading to sam-s3input-sample/f046ce4caxxxxxxxxx52172a4585fd2a  195 / 195  (100.00%)

        Deploying with following values
        ===============================
        Stack name                   : sam-s3input-sample
        Region                       : ap-northeast-1
        Confirm changeset            : True
        Disable rollback             : True
        Deployment s3 bucket         : aws-sam-cli-managed-default-samclisourcebucket-bxxxxrv3w3n
        Capabilities                 : ["CAPABILITY_IAM", "CAPABILITY_AUTO_EXPAND"]
        Parameter overrides          : {"BucketName": "sam-s3input-sample-v1"}
        Signing Profiles             : {}

Initiating deployment
=====================
Uploading to sam-s3input-sample/66f6ed49exxxxxxxxxx70332f0c86.template  1375 / 1375  (100.00%)

Waiting for changeset to be created..
CloudFormation stack changeset
-------------------------------------------------------------------------------------------------------------------------------------------------------------
Operation                               LogicalResourceId                       ResourceType                            Replacement                           
-------------------------------------------------------------------------------------------------------------------------------------------------------------
+ Add                                   HelloWorldFunctionRole                  AWS::IAM::Role                          N/A                                   
+ Add                                   HelloWorldFunctionS3UploadEventPermis   AWS::Lambda::Permission                 N/A                                   
                                        sion                                                                                                                  
+ Add                                   HelloWorldFunction                      AWS::Lambda::Function                   N/A                                   
+ Add                                   S3InputBucket                           AWS::S3::Bucket                         N/A                                   
-------------------------------------------------------------------------------------------------------------------------------------------------------------

Changeset created successfully. arn:aws:cloudformation:ap-northeast-1:XXXXXXXXXXXX:changeSet/samcli-deploy165xxxx622/8df4ca2c-xxxx-xxxx-90a4-7b618f69cd16


Previewing CloudFormation changeset before deployment
======================================================
Deploy this changeset? [y/N]: y

2022-07-24 06:40:07 - Waiting for stack create/update to complete

CloudFormation events from stack operations (refresh every 0.5 seconds)
-------------------------------------------------------------------------------------------------------------------------------------------------------------
ResourceStatus                          ResourceType                            LogicalResourceId                       ResourceStatusReason                  
-------------------------------------------------------------------------------------------------------------------------------------------------------------
CREATE_IN_PROGRESS                      AWS::IAM::Role                          HelloWorldFunctionRole                  -                                     
CREATE_IN_PROGRESS                      AWS::IAM::Role                          HelloWorldFunctionRole                  Resource creation Initiated           
CREATE_COMPLETE                         AWS::IAM::Role                          HelloWorldFunctionRole                  -                                     
CREATE_IN_PROGRESS                      AWS::Lambda::Function                   HelloWorldFunction                      -                                     
CREATE_IN_PROGRESS                      AWS::Lambda::Function                   HelloWorldFunction                      Resource creation Initiated           
CREATE_COMPLETE                         AWS::Lambda::Function                   HelloWorldFunction                      -                                     
CREATE_IN_PROGRESS                      AWS::Lambda::Permission                 HelloWorldFunctionS3UploadEventPermis   -                                     
                                                                                sion                                                                          
CREATE_IN_PROGRESS                      AWS::Lambda::Permission                 HelloWorldFunctionS3UploadEventPermis   Resource creation Initiated           
                                                                                sion                                                                          
CREATE_COMPLETE                         AWS::Lambda::Permission                 HelloWorldFunctionS3UploadEventPermis   -                                     
                                                                                sion                                                                          
CREATE_IN_PROGRESS                      AWS::S3::Bucket                         S3InputBucket                           -                                     
CREATE_FAILED                           AWS::S3::Bucket                         S3InputBucket                           sam-s3input-sample-v1 already exists  
CREATE_FAILED                           AWS::CloudFormation::Stack              sam-s3input-sample                      The following resource(s) failed to   
                                                                                                                        create: [S3InputBucket].              
-------------------------------------------------------------------------------------------------------------------------------------------------------------

Failed to deploy. Automatic rollback disabled for this deployment.

Actions you can take next
=========================
[*] Fix issues and try deploying again
[*] Roll back stack to the last known stable state: aws cloudformation rollback-stack --stack-name sam-s3input-sample

Error: Failed to create/update the stack: sam-s3input-sample, Waiter StackCreateComplete failed: Waiter encountered a terminal failure state: For expression "Stacks[].StackStatus" we matched expected path: "CREATE_FAILED" at least once
$ 
```

## 動作確認

それではデプロイが問題なく完了したら構築されたS3に実際にファイルを置いて動作確認をしてみましょう。
S3バケット名は、デフォルト値かデプロイの時に入力したバケット名になります。
デプロイコマンドの最後に出力されている以下が作成されたバケット名になります。（template.yamlでOutputsに指定した内容になります）

```
Key                 S3InputBucketArn                                                                                                                         
Description         Hello World S3 ARN                                                                                                                       
Value               arn:aws:s3:::sam-s3input-sample-v1                                                                                                       
```

問題なく構築出来ていれば、S3バケットが以下のように作成されているはずです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/188544/54ed0dff-9cf9-33a1-e25a-d41fddfef26d.png)


それでは、試しに **test.txt** という0バイトファイルをAWSコンソールからアップロードしてみます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/188544/2028df9e-0b1c-fe50-c884-92d46e5cfb83.png)

上記のようにS3バケットのファイルアップロードを行うと、正しく構築に成功していれば、**CloudWatch**の**ロググループ**に以下にようなLambda関数のロググループが作成されています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/188544/4fdcf371-6016-30fb-b001-a6067c597f4f.png)


ロググループをクリックしていくと、以下のようなログが出力されていることが確認でき、

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/188544/14fc331e-a54c-460b-9b7a-dbe727e3b3cb.png)

event内を出力したログ（上から2行目のログ）をクリックすると、
確かにS3バケットにアップロードしたファイル名が **test.txt** だったことなどが確認できると思います。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/188544/f6ee92ec-2e06-ef1d-d81a-86e7db2c5fde.png)


以上！非常に簡単だったのではないでしょうか。
これでAWSコンソールの画面UIが変わるたびに設定内容に迷う必要がなくなりました(^^)

## 補足：削除

今回は削除にも触れておきます。
SAMを利用していると削除は簡単
　**sam delete**
で、可能です。

```sh
$ sam delete
        Are you sure you want to delete the stack sam-s3input-sample in the region ap-northeast-1 ? [y/N]: y
        Are you sure you want to delete the folder sam-s3input-sample in S3 which contains the artifacts? [y/N]: y
        - Deleting S3 object with key sam-s3input-sample/f046ce4caxxxxxXXXXXXX2172a4585fd2a
        - Deleting S3 object with key sam-s3input-sample/66f6ed49ecaxxxxxXXXXXXX2170332f0c86.template
        - Deleting Cloudformation stack sam-s3input-sample
Error: Stack could not be deleted as it encountered DELETE_FAILED status: sam-s3input-sample, ex: Waiter StackDeleteComplete failed: Waiter encountered a terminal failure state: For expression "Stacks[].StackStatus" we matched expected path: "DELETE_FAILED" at least once
$
```

ただし、上の例のように状況によっては失敗します。。。

**Error: Stack could not be deleted as it encountered DELETE_FAILED status: sam-s3input-sample, ex: Waiter StackDeleteComplete failed: Waiter encountered a terminal failure state: For expression "Stacks[].StackStatus" we matched expected path: "DELETE_FAILED" at least once**

原因はS3バケットにファイルが残っているケースです。
ファイルがあるとS3バケットが削除できないので、動作確認とかでファイルを作成していれば以下の状態になるまで手動削除しておく必要があります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/188544/b594956c-cc45-6cc0-8e44-0fd623860ea4.png)


この状態で削除コマンドを再実行すれば今度は成功します。

```sh
$ sam delete
        Are you sure you want to delete the stack sam-s3input-sample in the region ap-northeast-1 ? [y/N]: y
        Are you sure you want to delete the folder sam-s3input-sample in S3 which contains the artifacts? [y/N]: y
        - Could not find and delete the S3 object with the key sam-s3input-sample/f046ce4caxxxxxXXXXXXX2172a4585fd2a
        - Deleting Cloudformation stack sam-s3input-sample

Deleted successfully
$
```

以上です！


## まとめ
お疲れ様でした！

今回はLambdaでsamを使って**Lambda × S3**の環境を構築してみました。環境設定を **template.yml** に実処理を**app.py**に記載できて、後はコマンド実行で完了するので、再現性が高く、ファイル群はGitなどで履歴管理できるので非常に便利になると思います。
samを使ってプログラム集中出来るサーバーレスLambdaがもっと便利になって、開発者はより業務ロジックに集中出来る世の中になれば良いなと思います(^^)

本投稿が少しでも皆さんの良いサーバーレスライフの助けになればと思います(_ _)

## 資格取得に向けてAWSサービスを実際に利用してみるシリーズの投稿一覧

[とりあえず30分でAWS利用料金の高額請求に備える～予算アラート設定・MFA・料金確認～](https://qiita.com/fkooo/items/67c53c997508e25605ea "とりあえず30分でAWS利用料金の高額請求に備える～予算アラート設定・MFA・料金確認～")
[AWS ECSでDocker環境を試してみる](https://qiita.com/fkooo/items/d7d98e16e91921508450 "AWS ECSでDocker環境を試してみる")
[Amazon Cognitoを使ってシンプルなログイン画面を作ってみる](https://qiita.com/fkooo/items/660cab0090a80861155b "Amazon Cognitoを使ってシンプルなログイン画面を作ってみる")
[AWS NATゲートウェイを使ってプライベートサブネットからインターネットにアクセスする](https://qiita.com/fkooo/items/a474db0f3c2e72448d36 "AWS NATゲートウェイを使ってプライベートサブネットからインターネットにアクセスする")
[API GatewayをPrivateで作成してみる](https://qiita.com/fkooo/items/577831abd9803eb91b16 "API GatewayをPrivateで作成してみる")
[AWSのAI(Rekognition/Polly/Transcribe/Comprehend/Translate/Textract)サービスを試してみる](https://qiita.com/fkooo/items/10f75bafd08c60fc0a66 "AWSのAI(Rekognition/Polly/Transcribe/Comprehend/Translate/Textract)サービスを試してみる")
[AWS Lambda 同時実行数、エイリアス、環境変数とか実際の現場で使える機能を勉強してみる](https://qiita.com/fkooo/items/1b24e01252e145a378db "AWS Lambda 同時実行数、エイリアス、環境変数とか実際の現場で使える機能を勉強してみる")
[AWS Lambda SAMとは？～Cloud9でSAM Sampleを使ってPythonのLambdaプログラムを簡単にデプロイする～](https://www.studycloudtech.com/aws/lambda-sam-start-cloud9/ "AWS Lambda SAMとは？～Cloud9でSAM Sampleを使ってPythonのLambdaプログラムを簡単にデプロイする～")
[AWS SAMを使って最もシンプルにLambda × APIGatewayのWebAPIを構築する](https://qiita.com/fkooo/items/e08cdea0f73f60dabd3d "AWS SAMを使って最もシンプルにLambda × APIGatewayのWebAPIを構築する")
[AWS SAMを使って最もシンプルにLambda × S3 のS3イベント駆動プログラムを構築する](https://qiita.com/fkooo/items/fa1d03c1d9e532956e2d "AWS SAMを使って最もシンプルにLambda × S3 のS3イベント駆動プログラムを構築する")（本記事）