SAM meets CDK？

ハロー、AWS！ 君はもう、バッチリCDKでStackを育てたかい？
たっぷり作り込んだ君もまだまだの君も、IaCの歓びに貴賤はない！　一緒にノウハウを分かち合おう！
今回はCDKの運用にAWS SAMを絡めるとどーなるのか、いってみよう！

CDKの基本については[このへん](https://docs.aws.amazon.com/cdk/api/latest/)を頭から読んでもらうのがイチバンだけど、SAMやCFnに不慣れな場合には、かんたんにでも[CFn](https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/Welcome.html)から始めてもらう方がいいかも知れないね。

## 先にまとめ

* StackおよびLambda関数のコードはCDKで管理
* 主にローカルでのデバッグ、実行にSAM CLIを使う
    * CDKから`synth`してCFnテンプレートを生成した後、`sam local`系のコマンドが使える
        * invoke
        * start-lambda
        * start-api (`AWS::Serverless::Function`リソースの定義が必要)
* その他、LambdaのパッケージングやデプロイはCDKから実行する・・・ことになりそう

## そもそもSAMでできることおさらい
1. CFnリソースタイプの拡張機能
    * 統合されたAPI Gateway + Lambdaのリソースタイプ等
    * CDKで言うところの`AWS Construct`に近いもの
    * [このへん](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-template.html)
2. Lambda関数およびAPI Gatewayをローカル環境にデプロイ、実行する機能
    * ランタイム環境のコンテナ作成と実行
3. 定義したLambda関数および周辺リソースを含めたStackのデプロイ
4. その他
    * AWS Serverless Application Repository にpublishする機能とか

この中で、CDKは1, 3の機能をカバーしている（要プラグイン）から、CDKからSAMが2の機能を実行するために使えるCFnテンプレートを生成して、それをSAM CLIで使うことになるんだよ。ワクワクするね！

## やってみた

まずはサンプルプロジェクトの雛形を作ろう。今回はpythonを使うよ。

```bash
$ mkdir cdk-sam-sample
$ cd cdk-sam-sample
$ cdk init app --language=python
```

virtualenvで環境を切り替えて、`aws-sam`パッケージをインストールしよう。

```bash
$ source .env/bin/activate
(.env) $ pip install -r requirements.txt
```

requirements.txtはこんな感じで。

```
-e .
aws-cdk.aws-sam
```

生成されるpythonのソースファイルはこんな感じだ。

##### app.py

```python
#!/usr/bin/env python3    
    
from aws_cdk import core    
    
from cdk_sam_sample.cdk_sam_sample_stack import CdkSamSampleStack    
    
    
app = core.App()    
CdkSamSampleStack(app, "cdk-sam-sample")    
    
app.synth() 
```

生成されたpythonのスタック定義に、SAMの拡張リソースを作ってみよう！

##### cdk_sam_sample/cdk_sam_sample_stack.py

```python
from aws_cdk import core, aws_sam as sam    
    
    
class CdkSamSampleStack(core.Stack):    
    def __init__(self, scope: core.Construct, id: str, **kwargs) -> None:    
        super().__init__(scope, id, **kwargs)    
    
        function = sam.CfnFunction(    
            self,    
            "HelloWorldFunction",    
            runtime="python3.7",    
            handler="app.lambda_handler",    
            code_uri="lambda_functions/",    
            events={    
                "HelloWorld": {    
                    "type": "Api",    
                    "properties": {"method": "get", "path": "/hello"},    
                }    
            },    
        ) 
```

CDKのコードが準備できたら、今度はLambda関数を準備するよ。

```bash
$ mkdir lambda_functions
```

##### lambda_functions/app.py

```python
import json

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

さぁ、準備はいいかな？ 早速、テンプレートを生成してみよう！

```bash
(.env) $ cdk synth --no-staging > template.yml
```

##### template.yml

```yml
Transform: AWS::Serverless-2016-10-31
Resources:
  HelloWorldFunction:
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: lambda_functions/
      Handler: app.lambda_handler
      Runtime: python3.7
      Events:
        HelloWorld:
          Properties: {}
          Type: Api
    Metadata:
      aws:cdk:path: cdk-sam-sample/HelloWorldFunction
  CDKMetadata:
    Type: AWS::CDK::Metadata
    Properties:
      Modules: aws-cdk=1.6.0,@aws-cdk/aws-sam=1.6.0,@aws-cdk/core=1.6.0,@aws-cdk/cx-api=1.6.0,jsii-runtime=Python/3.7.4
```

（2019-08-29現在）`Properties`が意図した通り生成されないようなので、下記のように修正。

```yml
Properties:    
  Path: /hello             
  Method: get
```

ちゃんとテンプレートは生成できたかな？ いよいよ、SAM CLIでローカル環境にAPIサーバを起動するよ！

```bash
$ sam local start-api
```

この状態で、`http://127.0.0.1:3000/hello`にGETリクエストを送ると、コンテナが起動してレスポンスが返ってくるはずだ。

```bash
$ curl http://127.0.0.1:3000/hello
{"message": "hello world"}
```

やったね！

## ( ・∀・)ｲｲ!!

* CDKでSAMの拡張リソースタイプも使える
* API Gateway + Lambdaのローカル環境が簡単に作れる
* SAMではやりきれなかったStackの管理がCDKでできる

## ( ・A・)ｲｸﾅｲ!!

* `aws_cdk.aws_sam`パッケージがまだベータ版
    * `Properties`フィールドが上手く生成されないので、まだ使えない・・・

## こーするのがいいと思う

* Stackの管理、運用はCDKに乗っていくのが良さそう
* SAMの拡張も使えるけど、もうちょい様子見
* ローカル開発環境はSAM CLIでサクサク作っていこう


**使い終わったStackは、30分以内に`cdk destroy`だ！**