【AWS】SAMを使ってみよう （デプロイ編）

# はじめに
こんにちは。最近SAMを使い始めて、魅了されています。
興味がある方の参考になればと思い投稿します。
今回は、「SAMのセットアップ〜AWSへのデプロイの一連の流れ」のデプロイ編になります。
下記の悩みを持っている方は是非、見てください。

- SAMって聞いたことあるけど、触ったことない
- APIGateway、Lambdaなどサーバレスアーキテクチャーは構築したことはあるが、AWSコンソール上で作成しており、一元管理できていない。ローカル開発をしてgit管理しながらデプロイしたい

# 事前準備
1.  先に、デプロイ対象となるsamプロジェクトを作成してください。必要に応じて下記をご参照ください。前提は、ランタイムがNode20のTypeScriptを使用しています。ディレクトリ構造は下記とします。
```
.
└── sample-sam
    ├── README.md
    ├── events
    │   └── event.json
    ├── hello-world
    │   ├── app.ts //ここがエンドポイント
    │   ├── jest.config.ts
    │   ├── package.json
    │   ├── tests
    │   │   └── unit
    │   │       └── test-handler.test.ts
    │   └── tsconfig.json
    ├── samconfig.toml //samの設定ファイル
    └── template.yaml //リソースの設定ファイル（≒ cloudFormation）

```

[【AWS】SAMを使ってみよう （セットアップ編）](https://qiita.com/baison_/items/99b98718a0424f33a530)
2. AWSアカウントの作成、IAMユーザーの作成をお願いします。(CLIからデプロイしたい方は、アクセスキーの作成もお願いします。※セキュリティにはお気をつけください)

# GUIベースでデプロイ
### 全体の実施事項
- ローカルでTypeScriptをトランスパイルして、S3バケットに格納
- cloudFormationから、デプロイ

##### 1. まずは、TypeScriptのコードをトランスパイル
```
cd hello-world && tsc app.ts
```

##### 2. 作成されたapp.jsのZIPファイルを作成
```
zip -r function.zip ./app.js
```
※今回は、aws-sdk以外の依存がないため、app.jsのみが必要。依存がある場合は、依存も含めてZIP化

##### 3. 作成されたfunction.zipをS3に保存
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3983223/af7a1dcf-eddc-ba1a-857e-3ea75372ddb4.png)

##### 4. 上記作成のS3オブジェクトのUriをコピー
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3983223/72fb8c23-f1ca-f71e-f95a-6c39856cbe05.png)

##### 5. template.yamlのCodeUriを変更
```
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: >
  sample-sam2
  
  Sample SAM Template for sample-sam2
Globals:
  Function:
    Timeout: 3

Resources:
  HelloWorldFunction:
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: s3://test-baison/function.zip //ここにS3のUriを指定
      Handler: app.lambdaHandler
      Runtime: nodejs20.x
      Architectures:
        - x86_64
      Events:
        HelloWorld:
          Type: Api
          Properties:
            Path: /hello
            Method: get
    Metadata:
      BuildMethod: esbuild
      BuildProperties:
        Minify: true
        Target: 'es2020'
        Sourcemap: true
        EntryPoints:
          - app.ts
```

##### 6. cloudFormationのサービスから、template.yamlをUPLoadしてスタックを作成
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3983223/366c8a22-34e6-0c2f-7b95-aa67526e5371.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3983223/a7e0aebd-341f-442a-8f38-94ab3248f6d5.png)

スタック名を適当に決めて、下記のチェックを入れたら、あとはそのままでスタックを作成
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3983223/a38a6fdb-5fcc-8215-d7e9-5dd7da3f7c84.png)

##### 7. 無事にスタックが作成されたらAPIGatewayの自動で作成されたドメインへアクセス
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3983223/93823d09-a711-d8ae-bc30-97e0fac909df.png)

##### 8. 無事にアクセスできたら、デプロイ成功
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3983223/7703ef39-270d-04cf-c315-426b9b359217.png)

※この時点では、APIGatewayにIP制限をしていないため、世界中どこからでもアクセスできる仕様であることに注意。IP制限の方法は下に記載。


# CLIベースでデプロイ
### 全体実施事項
- AWSの認証情報の設定
- コマンドラインからデプロイ
※IAMユーザーのアクセスキーは漏洩すると、非常にセキュリティ上危ないので**十分に気をつけてください**。AWSのベストプラクティスとしても、アクセスキーは必要最低限以外は使用を推奨してません。STSでの一時認証（GithubActions等のAssumeロールで実施）の手段もご検討ください。

##### 1. IAMアクセスキーを使用して、認証情報を設定


##### 2. 必要に応じて、tempalate.yamlの編集（IP制限不要の場合は省略可能）
```
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: >
  sample-sam
  
  Sample SAM Template for sample-sam

Globals:
  Function:
    Timeout: 3

Resources:
  ApiGatewayApi: //IP制限をしたい場合は、APIGatewayのリソースポリシーに記載が必要。そのため、APIGatewayのリソースを追記
    Type: AWS::Serverless::Api
    Properties:
      StageName: Prod
      Auth:
        ResourcePolicy:
          CustomStatements:
            - Effect: Allow
              Principal: '*'
              Action: execute-api:Invoke
              Resource: execute-api:/*
              Condition:
                IpAddress:
                  aws:SourceIp: '*.*.*.*/32'
            - Effect: Deny
              Principal: '*'
              Action: execute-api:Invoke
              Resource: execute-api:/*
              Condition:
                NotIpAddress:
                  aws:SourceIp: '*.*.*.*/32'
  HelloWorldFunction:
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: hello-world
      Handler: app.lambdaHandler
      Runtime: nodejs20.x
      Architectures:
        - x86_64
      Events:
        HelloWorld:
          Type: Api
          Properties:
            Path: /hello
            Method: get
            RestApiId: !Ref ApiGatewayApi //上記で追記した、APIGatewayの論理名を指定
    Metadata:
      BuildMethod: esbuild
      BuildProperties:
        Minify: true
        Target: 'es2020'
        Sourcemap: true
        EntryPoints:
          - app.ts
```

##### 3. sample-samディレクトリ直下で、buildとdeployを実施
```
sam build
sam deploy
```
途中でchagesetについて聞かれるが、問題なければyで進める。
※差分を聞かれるのが面倒くさい場合は、samconfig.tomlの
```
confirm_changeset = false
```
に設定しておく。

##### 4. 無事にスタックが作成されたら、GUIで実施した時同様に、APIGatewayの作成されたURLへアクセスして、デプロイを確認


# まとめ
SAM＆CLIを使えば、すごく簡単にサーバーレスアーキテクチャーをAWS上にデプロイすることができます。ご参考になれば幸いです。

# 関連記事（投稿後、リンクを追加します）
- [SAMを使ってみよう（セットアップ編）](https://qiita.com/baison_/items/99b98718a0424f33a530)
- SAMを使ってみよう（ローカル開発・3層アーキテクチャ編）　未