Hello AWS SAM World!!

# はじめに
この記事は [アイレット株式会社 22新卒 Advent Calendar 2022](https://qiita.com/advent-calendar/2022/iret-22) 2日目の記事になります。

# AWS SAMとは
AWS SAMとは、AWS サーバーレスアプリケーションモデル (Serverless Application Model)の略で、サーバーレスアプリケーションを構築するためのオープンソースフレームワークです。


![sam君.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/269896/9835c4e0-807f-35a2-3fa7-0fc72f632b16.png)
サムくん(勝手に呼んでる)かわいいよね。

# AWS SAMでHello World!
今回は、AWS Lambda上でHello Worldする関数を作成します。

自宅のPCはWindowsなので[Windows用のドキュメント](https://docs.aws.amazon.com/ja_jp/serverless-application-model/latest/developerguide/serverless-sam-cli-install-windows.html)に従ってやっていきます。
Macの方はこちら→[Mac用のドキュメント](https://docs.aws.amazon.com/ja_jp/serverless-application-model/latest/developerguide/serverless-sam-cli-install-mac.html)

# AWS SAM CLIのインストール
Windowsの人は[ここ](https://github.com/aws/aws-sam-cli/releases/latest/download/AWS_SAM_CLI_64_PY3.msi)からダウンロードできます。

msiファイルを実行して、インストールが完了したら

```bash
>> sam --version
```
正常にインストールされていたら
```bash
SAM CLI, version 1.59.0
```
みたいな出力が表示されると思います。


# アプリケーションのデプロイ

## sam init
```bash
>> sam init
```
今回はHello Worldがしたいので AWS Quick Start Templates で　Hello World Example を選択します。

出力
```bash
You can preselect a particular runtime or package type when using the `sam init` experience.
Call `sam init --help` to learn more.

Which template source would you like to use?
        1 - AWS Quick Start Templates
        2 - Custom Template Location
Choice: 1

Choose an AWS Quick Start application template
        1 - Hello World Example
        2 - Multi-step workflow
        3 - Serverless API
        4 - Scheduled task
        5 - Standalone function
        6 - Data processing
        7 - Infrastructure event management
        8 - Lambda EFS example
        9 - Machine Learning
Template: 1

Use the most popular runtime and package type? (Python and zip) [y/N]: y

Would you like to enable X-Ray tracing on the function(s) in your application?  [y/N]: N

Project name [sam-app]: hello-world

Cloning from https://github.com/aws/aws-sam-cli-app-templates (process may take a moment)

    -----------------------
    Generating application:
    -----------------------
    Name: hello-world
    Runtime: python3.9
    Architectures: x86_64
    Dependency Manager: pip
    Application Template: hello-world
    Output Directory: .

    Next steps can be found in the README file at ./hello-world/README.md


    Commands you can use next
    =========================
    [*] Create pipeline: cd hello-world && sam pipeline init --bootstrap
    [*] Validate SAM template: cd hello-world && sam validate
    [*] Test Function in the Cloud: cd hello-world && sam sync --stack-name {stack-name} --watch
```

実行が終わると大体以下のようなファイルが生成されていると思います。

```
 hello-world/
   ├── README.md
   ├── events/
   │   └── event.json
   ├── hello_world/
   │   ├── __init__.py
   │   ├── app.py            
   │   └── requirements.txt  
   ├── template.yaml         
   └── tests/
       └── unit/
           ├── __init__.py
           └── test_handler.py
```

以下の3つのファイルが特に重要な役割のものです。

template.yaml: AWSリソースの定義をするファイル
hello_world/app.py: 実際のLambdaハンドラーのロジックを記述するファイル
hello_world/requirements.txt:アプリケーションが必要とするPython依存関係を記述するファイル

## 各ファイルの中身
**テンプレートファイル**
```template.yaml
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: >
  hello-world

  Sample SAM Template for hello-world

# More info about Globals: https://github.com/awslabs/serverless-application-model/blob/master/docs/globals.rst
Globals:
  Function:
    Timeout: 60

Resources:
  HelloWorldFunction:
    Type: AWS::Serverless::Function # More info about Function Resource: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#awsserverlessfunction
    Properties:
      CodeUri: hello_world/
      Handler: app.lambda_handler
      Runtime: python3.9
      Architectures:
        - x86_64
      Events:
        HelloWorld:
          Type: Api # More info about API Event Source: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#api
          Properties:
            Path: /hello
            Method: get

Outputs:
  # ServerlessRestApi is an implicit API created out of Events key under Serverless::Function
  # Find out more about other implicit resources you can reference within SAM
  # https://github.com/awslabs/serverless-application-model/blob/master/docs/internals/generated_resources.rst#api
  HelloWorldApi:
    Description: "API Gateway endpoint URL for Prod stage for Hello World function"
    Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/hello/"
  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

```

上記コードは大きくGlobals,Resources,Outputsに分けられます。

Globals:アプリケーションに複数のFunctionが含まれる場合,そのすべてに共通する設定を記述する部分
Resources:各Functionごとの設定を記述する部分
Outputs:AWS SAMから出てくるものの定義。 エンドポイントなど。



**関数本体**
``` app.py
import json

def lambda_handler(event, context):
    return {
        "statusCode": 200,
        "body": json.dumps({
            "message": "hello world",
        }),
    }
```

requirements.txtは今回必要ないため割愛。
外部ライブラリが必要な場合は以下のように記述します。
```txt
#version指定がないと最新版をダウンロードしてくる
requests

#バージョン指定がある場合はその条件に従ってダウンロードしてくれる。
pytest >= 5.3.5
pandas == 1.2.4
```

## ビルド
先ほど、sam initで作成したhello-worldディレクトリに移動して以下のコマンドを実行します。
```bash
>> sam build
```
```bash
Your template contains a resource with logical ID "ServerlessRestApi", which is a reserved logical ID in AWS SAM. It could result in unexpected behaviors and is not recommended.
Building codeuri: F:\AWS\hello-world\hello_world runtime: python3.8 metadata: {} architecture: x86_64 functions: HelloWorldFunction
Running PythonPipBuilder:ResolveDependencies
Running PythonPipBuilder:CopySource

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
```
ビルドに成功すると、次に実行できるコマンドのリストが出てくるのでいったんローカルで実行をテストしてみます。
## ローカルテスト
**※ローカルで関数を実行するためにはDockerが必要です。**

導入方法
https://docs.docker.com/docker-for-windows/install/


Dockerでコンテナを立ち上げてから以下のコマンドを実行します。
```bash
>> sam local invoke
```
出力
```
START RequestId: bfd5e63c-6a78-4970-9445-d363c1a6ceed Version: $LATEST
END RequestId: bfd5e63c-6a78-4970-9445-d363c1a6ceed
REPORT RequestId: bfd5e63c-6a78-4970-9445-d363c1a6ceed  Init Duration: 0.14 ms  Duration: 133.35 ms     Billed Duration: 134 ms Memory Size: 128 MB     Max Memory Used: 128 MB
{"statusCode": 200, "body": "{\"message\": \"hello world\"}"}
```

このようにデプロイを行う前にローカルでテストすることができます。
## デプロイ
テストもできたので最後にデプロイします。
```bash
>> sam deploy --guided
```

出力
```bash
CloudFormation stack changeset
---------------------------------------------------------------------------------------------------------------------
Operation                     LogicalResourceId             ResourceType                  Replacement
---------------------------------------------------------------------------------------------------------------------
+ Add                         HelloWorldFunctionHelloWorl   AWS::Lambda::Permission       N/A
                              dPermissionProd
+ Add                         HelloWorldFunctionRole        AWS::IAM::Role                N/A
+ Add                         HelloWorldFunction            AWS::Lambda::Function         N/A
+ Add                         ServerlessRestApiDeployment   AWS::ApiGateway::Deployment   N/A
                              47fc2d5f9d
+ Add                         ServerlessRestApiProdStage    AWS::ApiGateway::Stage        N/A
+ Add                         ServerlessRestApi             AWS::ApiGateway::RestApi      N/A
---------------------------------------------------------------------------------------------------------------------

Changeset created successfully. arn:aws:cloudformation:ap-northeast-1:222074562886:changeSet/samcli-deploy1669567629/e78e49c6-7c15-464b-b4fd-cd3dd97cfa3f


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

2022-11-28 01:47:20 - 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::CloudFormation::Stack    sam-app                       User Initiated
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::ApiGateway::RestApi      ServerlessRestApi             -
CREATE_IN_PROGRESS            AWS::ApiGateway::RestApi      ServerlessRestApi             Resource creation Initiated
CREATE_COMPLETE               AWS::ApiGateway::RestApi      ServerlessRestApi             -
CREATE_IN_PROGRESS            AWS::ApiGateway::Deployment   ServerlessRestApiDeployment   -
                                                            47fc2d5f9d
CREATE_IN_PROGRESS            AWS::Lambda::Permission       HelloWorldFunctionHelloWorl   -
                                                            dPermissionProd
CREATE_IN_PROGRESS            AWS::Lambda::Permission       HelloWorldFunctionHelloWorl   Resource creation Initiated
                                                            dPermissionProd
CREATE_IN_PROGRESS            AWS::ApiGateway::Deployment   ServerlessRestApiDeployment   Resource creation Initiated
                                                            47fc2d5f9d
CREATE_COMPLETE               AWS::ApiGateway::Deployment   ServerlessRestApiDeployment   -
                                                            47fc2d5f9d
CREATE_IN_PROGRESS            AWS::ApiGateway::Stage        ServerlessRestApiProdStage    -
CREATE_IN_PROGRESS            AWS::ApiGateway::Stage        ServerlessRestApiProdStage    Resource creation Initiated
CREATE_COMPLETE               AWS::ApiGateway::Stage        ServerlessRestApiProdStage    -
CREATE_COMPLETE               AWS::Lambda::Permission       HelloWorldFunctionHelloWorl   -
                                                            dPermissionProd
CREATE_COMPLETE               AWS::CloudFormation::Stack    sam-app                       -
---------------------------------------------------------------------------------------------------------------------
CloudFormation outputs from deployed stack
---------------------------------------------------------------------------------------------------------------------
Outputs
---------------------------------------------------------------------------------------------------------------------
Key                 HelloWorldFunctionIamRole
Description         Implicit IAM Role created for Hello World function
Value               #######################################

Key                 HelloWorldApi
Description         API Gateway endpoint URL for Prod stage for Hello World function
Value               #######################################

Key                 HelloWorldFunction
Description         Hello World Lambda Function ARN
Value               #######################################
---------------------------------------------------------------------------------------------------------------------
```

30秒ほどでAWS上にデプロイができました。

簡単に出来過ぎて本当にできてるのか心配なので試しに叩いてみます。

```bash
>> curl https://c28gdrpdtb.execute-api.ap-northeast-1.amazonaws.com/Prod/hello/
{"message": "hello world"}
```
APIを叩いて値を持ってくることができました🙌

# さいごに
今回はHello World関数だけの簡単な構成でしたが、もっと複雑になった際にこんな感じでIac化しておくと管理が楽になるなぁと感じました。
個人的にAWS Lambdaはお気に入りサービスなので、これからSAMをバリバリ活用して開発していこうと思います。

[アイレット株式会社 22新卒 Advent Calendar 2022](https://qiita.com/advent-calendar/2022/iret-22) 3日目は[Miuraくん](https://qiita.com/dengzais8)が担当です! お楽しみに！

# おまけ
先日、社内Slackでサム君(リス)かわいいってつぶやいていたら、上司の方からAWS SAMのステッカーをいただきました🥰
![IMG_9010.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/269896/01fb7c82-a7aa-a04c-c1c7-8ec128b2ea93.jpeg)

この世にはサム君のTシャツも存在しているみたいなのでいつか手に入れたい・・・
https://twitter.com/awscloud/status/857670730464370688!
![C-cO-wPUAAAQUee.jfif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/269896/eca4f0d7-3d1f-9deb-7b34-0599b6c5dfde.jpeg)