AWS SAM with CDK

## 概要
先日`AWS CDK Conference Japan 2022`でCDK開発にSAMを取り入れている方がいらっしゃいました。SAMはなくなんとなーく知っている程度だったのですが、CDKのローカルテストに使うことができたりと面白そうだなと思ったのでサービスの概要からCDKで作成したテンプレートをSAM CLIでテストするまでの流れについて調べてみました。


## AWS SAMとは

結論から言うとSAMはCloudFormationのTransform機能です。これを聞いて納得した方はこの章はスキップしてください。以下ではもう少し噛み砕いて説明します。

AWS SAMとはServerless Application Modelと呼ばれるAWSが提供するIaCサービスです。SAMはサーバーレスサービスに特化したIaCでYAMLとJSON形式をサポートしています。例えばサーバーレスの代表格であるLambdaをデプロイしたいときは次のように書けばOKです。

```yaml
AWSTemplateFormatVersion: 2010-09-09
Transform: AWS::Serverless-2016-10-31
Resources:
  MyServerlessFunctionLogicalID:
    Type: AWS::Serverless::Function
    Properties:
      Handler: index.handler
      Runtime: python3.9
      CodeUri: 's3://test-bucket-20220507/sampleLambda.zip'
```

2行目にも書かれていますが、SAMの実態はCloudFormationのTransform機能です。わざわざTransformを使用するのはCloudFormationで書く場合に比べると書くコード量が減らせるからです。

これを確かめるために先ほどのコードからスタックをデプロイしてみます。

![スクリーンショット 2022-05-07 20.43.48.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1654639/95967515-7be7-699f-2400-3637abe1c101.png)

作成されたスタックを見てみるとLambda関数だけでなくIAMロールも作成されています。ロールの中身はLambda関数の実行ロール`AWSLambdaBasicExecutionRole`で、作成したLambdaに紐づいていました。

![スクリーンショット 2022-05-07 20.58.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1654639/5c3fe9d4-b3e0-f02b-a656-23979b06dcd4.png)


一方、SAMを使わずに生のCloudFormationで記述した場合、次のようになります。

```yaml
AWSTemplateFormatVersion: 2010-09-09
Resources:
  MyServerlessFunctionLogicalID:
    Type: 'AWS::Lambda::Function'
    Properties:
      Code:
        S3Bucket: test-bucket-20220507
        S3Key: sampleLambda.zip
      Handler: index.handler
      Role: !GetAtt 
        - MyServerlessFunctionLogicalIDRole
        - Arn
      Runtime: python3.9
      Tags:
        - Key: 'lambda:createdBy'
          Value: SAM
  MyServerlessFunctionLogicalIDRole:
    Type: 'AWS::IAM::Role'
    Properties:
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Action:
              - 'sts:AssumeRole'
            Effect: Allow
            Principal:
              Service:
                - lambda.amazonaws.com
      ManagedPolicyArns:
        - 'arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole'
      Tags:
        - Key: 'lambda:createdBy'
          Value: SAM
```

SAMで書いた方がコード量が少ないことがわかりますね。Lambdaの他にもAPI GatewayやDynamoDBもサポートしています。また、SAMは生のCloudFormationと併用して使えるので必要に応じて使い分けることでコード量を節約することができます。


## SAM CLI

次にSAM CLIについて説明します。CDKと同じようにSAMにもCLIが存在していてアプリケーションの作成と管理が可能です。各OSごとのインストール手順については[こちら](https://docs.aws.amazon.com/ja_jp/serverless-application-model/latest/developerguide/serverless-sam-cli-install.html)に記載があります。Macの場合はbrew経由でインストールします。

```shell
brew tap aws/tap
brew install aws-sam-cli

# インストールの確認
sam --version
> SAM CLI, version 1.48.0
```

`sam init`を入力すると`Which template source would you like to use?`と聞かれるので1を選択すればクイックスタート可能なテンプレートを選ぶことができます。今回は`Hello World Example`を選択しました。すでにソースコードは完成しているので`sam build`でビルドすると`.aws-sam//build/template.yaml`が作成されます。

また`sam local`コマンドを使用することでlocalテストが可能です。

```shell
sam local invoke -e events/event.json HelloWorldFunction

> Invoking app.lambda_handler (python3.9)
Skip pulling image and use local one: public.ecr.aws/sam/emulation-python3.9:rapid-1.48.0-x86_64.

Mounting /Users/hogehoge/Documents/sam_test_dir/test_sam/.aws-sam/build/HelloWorldFunction as /var/task:ro,delegated inside runtime container
START RequestId: 5310fe15-ae09-4d5a-8c80-b3cc295e9cce Version: $LATEST
END RequestId: 5310fe15-ae09-4d5a-8c80-b3cc295e9cce
REPORT RequestId: 5310fe15-ae09-4d5a-8c80-b3cc295e9cce  Init Duration: 0.88 ms  Duration: 428.59 ms     Billed Duration: 429 ms    Memory Size: 128 MB     Max Memory Used: 128 MB
{"statusCode": 200, "body": "{\"message\": \"hello world\"}"}%    
```

引数に指定している`event.json`についても`sam local generate-event`コマンドを使用することで作成することが可能です。例えばS3のputをトリガーとするLambdaのevent.jsonが作成したい場合であれば次のようにリダイレクトすればすぐに作成可能です。

```shell
sam local generate-event s3 put > event.json
```

個人的にこれはとても便利だと思います。他にどのサービスが使用できるかについては`sam local generate-event -h`を参照ください。

## SAM CLI with CDK

さていよいよ本題です。CDKで作成したスタックをSAM CLIのlocalテストでテストします。具体的には`sam local invoke`コマンドの`-t`オプションの引数にcdk synthなどで生成されるテンプレート(`cdk.out/hogehogeStack.template.json`)を指定することでcdkで作成したyamlファイルをsamのcliでテストします。

```shell
sam local invoke -e events/event.json -t ./cdk.out/hogehogeStack.template.json CdkFunction
```