CloudFormation、キミにきめた！

# 0.はじめに
今年の7月にAWSアソシをギリで取得した初心者が、AWSでそれっぽいことをする[奮闘記](https://qiita.com/haman/items/3ab610dd4f3c00584dd0)の[続き](https://qiita.com/haman/items/0c7108ac12fef426adbe)の更に続き。
CloudFormationの便利さは勿論、そこまでの道のりも整備されていて、感動。

　　　　　　　<img width="300" alt="君に決めた_Cloud.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/384185/92a1ff76-94dd-3d0e-df5b-2dfd1ba205d4.png">





# 1. やりたいこと
みんなが<font color="Blue">**楽して**</font>、セキュアな仕組みを<font color="Blue">**確実に実装**</font>できるようにしたい。
これがすべて。

- 楽して：これでも充分楽なんだけど、それでももっと楽したいのが人間。
    - Lambdaに参考となるPythonコードをコピペしてきて…
    - VPCとかサブネットの設定もして…
    - API-GWも作成して…
    - おっと、APIのデプロイもしなきゃ…
- 確実に実装：
    - 設定するVPCとか間違えそう
    - サブネットとか設定しなそう。

それを実現してくれるのが、<font color="Blue">**CloudFormationくん。**</font>
AWSアソシにはちらっと登場してくる彼。
便利そうってことは知ってたけど、どのくらい便利かは知らなかった。
いざ。

# 2. できたもの（構成図＋α）
構成図に手順を含めるとこんな感じ。
青枠部分が今回楽して作りたいAPIGWとLambda。

　① 青枠のLambdaをテンプレ化して、
　② それをS3にぶち込んで(※1)、
　③ それをスタック作る際のインプットとする。：★
　  ※1 構成管理ってなら、CodeCommitなんだろう。。

ユーザは★をやるだけ。
そうすると、テンプレに従って、青枠の部分が勝手に作られる、そんな動き。

![slackAWSPOSTAPI-1-todo.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/384185/c36017b4-a95e-a0ba-a82a-8f5e13630461.png)


# 3. どんな風に作ってったか。
元となるLambdaとAPI-GWは前の旅で、作成済の状態。ざっとこんな状態。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/384185/0feeaf90-725f-8416-353e-d0c5e57aa15b.png)

## まずは、①のテンプレの元ネタを作ろう！
上記のLambdaの管理コンソール画面で、[アクション]→[関数のエクスポート]を選択すると、
以下２種類の形式でダウンロードできる。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/384185/9841244c-41bf-9b9f-a396-9fa01aaf6366.png)

ざっくり言うと、

- AWS SAMファイル：上の設定した環境変数やタグといった設定値が、超絶シンプルまとまって記載されたテンプートファイル
- デプロイパッケージ：ソースコードがZip化されたもの

今回は両方使う。
元ネタとしては、これで準備OK。
さらっと書いてるけど、これがあるのとないとでは、大違い。綺麗な道のり整備、助かります！

## ちょっとだけカスタマイズする。
いくつかコネコネする。
なお、前の手順でダウンロードしたSAMファイルはこんな状態。

```
AWSTemplateFormatVersion: '2010-09-09'
Transform: 'AWS::Serverless-2016-10-31'
Description: An AWS Serverless Specification template describing your function.
Resources:
  slackwebfiltertry:
    Type: 'AWS::Serverless::Function'
    Properties:
      Handler: lambda_function.lambda_handler
      Runtime: python3.8
      CodeUri: .
      Description: ''
      MemorySize: 128
      Timeout: 3
      Role: >-
        arn:aws:iam::0000000000:role/service-role/slack-webfilter-try-role-xxxxxxx
      Events:
        Api1:
          Type: Api
          Properties:
            Path: /slack-webfilter-try
            Method: POST
      VpcConfig:
        SecurityGroupIds:
          - sg-xxxxxxxxxxxxxxx
        SubnetIds:
          - subnet-xxxxxxxxxxxxxxx
          - subnet-xxxxxxxxxxxxxxx
      Environment:
        Variables:
          POST_SLACKAPI_URL: >-
            https://xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.amazonaws.com/xxxxxxxxxxxxxxxxxx/share-postanytoslack-api
          WEB_HOOK_URL: >-
            https://hooks.slack.com/services/xxxxxxxxxxxxxxxx/xxxxxxxxxxxxxxx/xxxxxxxxxxxxxxxx
      Tags:
        slack: 'true'
```


レッツ、コネコネタイム。

いや、待て待て。それぞれ何を定義してんのか分からんわ！（いや、なんとなく分かるかｗ）
ってことで、ブラックベルト。これは分かりやすい。
https://d1.awsstatic.com/webinars/jp/pdf/services/20190814_AWS-Blackbelt_SAM_rev.pdf

できそうなので、レッツ、コネコネ。

### (1) 関数コード
`CodeUri`が、ローカルの状態なので、S3上のデプロイパッケージのパスに置き換える。
※前の手順でダウンロードしたデプロイパッケージをS3にぶち込んでおく。

### (2) 作成するLambdaの関数名
デフォルトだと関数名はCloudFormationのマクロ側で勝手に設定されてしまう。
名前は大事だ。
ユーザに自分の関数名を入力してもらうために、
`FunctionName`を追記して、かつパラメータとして扱う記述をする。
それが`Parameters:`の部分。

```
AWSTemplateFormatVersion: '2010-09-09'
Transform: 'AWS::Serverless-2016-10-31'
Description: An AWS Serverless Specification template describing your function.
Parameters:
  FunctionName:
    Type: String
    Description: Enter the purpose of the Lambda-function as a name.
Resources:
  slackwebfiltertry:
    Type: 'AWS::Serverless::Function'
    Properties:
      Handler: lambda_function.lambda_handler
      Runtime: python3.8
      CodeUri: s3://XXXXXXXXXXXXX/XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX.zip
      FunctionName: !Sub "${FunctionName}"
      Description: ''
      MemorySize: 128
      Timeout: 3
（略）
```

### (3) プロキシ統合
今回のAPIは、管理コンソールの図のように「Lambdaプロキシ統合を使用」**しない**版で作成したい。（チェックない状態）
※上でダウンロードした状態のままだと、プロキシ統合した版（チェックある状態）で作成されてしまう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/384185/9cf167e4-ff56-2ce2-5129-16cf28b42064.png)


こちらと、ブラックベルトを参考に。
https://dev.classmethod.jp/articles/cloudformation_template_for_api_gateway_integration_to_lambda/
※プロキシ統合したい場合⇒ `type: "aws_proxy"`、したくない場合⇒`type: "aws"`

Black Beltより
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/384185/2c380c92-2ebc-fa20-2b29-909b9a2bb69d.png)



EventsブロックのPathとMethodいらないかな？って思ってけど、
必須らしく、下の方にあるpathsとの違いはよく分かってない。。。

```
（略）
      Events:
        Api1:
          Type: Api
          Properties:
            RestApiId: !Ref posttoslackApi
            Path: /posttoslack
            Method: POST
      VpcConfig:
（略）
      Tags:
        slack: 'true'
  posttoslackApi:
    Type: 'AWS::Serverless::Api'
    Properties:
      StageName: prod
      DefinitionBody:
        swagger: "2.0"
        paths:
          /posttoslack:
            post:
              x-amazon-apigateway-integration:
                uri: 
                  Fn::Sub: arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${slackwebfiltertry.Arn}/invocations
                httpMethod: "POST"
                type: "aws"
```

ということで、この３つでテンプレートは完了！
S3にぶち込む。

```
AWSTemplateFormatVersion: '2010-09-09'
Transform: 'AWS::Serverless-2016-10-31'
Description: An AWS Serverless Specification template describing your function.
Parameters:
  FunctionName:
    Type: String
    Description: Enter the purpose of the Lambda-function as a name.
Resources:
  slackwebfiltertry:
    Type: 'AWS::Serverless::Function'
    Properties:
      Handler: lambda_function.lambda_handler
      Runtime: python3.8
      CodeUri: s3://xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.zip
      FunctionName: !Sub "${FunctionName}"
      Description: ''
      MemorySize: 128
      Timeout: 3
      Role: >-
        arn:aws:iam::xxxxxxxxx:role/service-role/slack-webfilter-try-role-xxxxxxx
      Events:
        Api1:
          Type: Api
          Properties:
            RestApiId: !Ref posttoslackApi
            Path: /posttoslack
            Method: POST
      VpcConfig:
        SecurityGroupIds:
          - sg-xxxxxxxxx
        SubnetIds:
          - subnet-xxxxxxxxx
          - subnet-xxxxxxxxx
      Environment:
        Variables:
          POST_SLACKAPI_URL: >-
            https://xxxxxxxxxxxxxxxxxx.amazonaws.com/xxxxxxxxxxxxxx/share-postanytoslack-api
          WEB_HOOK_URL: >-
            https://hooks.slack.com/services/xxxxxxxxxxxxxxxx/xxxxxxxxxxxxxxx/xxxxxxxxxxxxxxxx
      Tags:
        slack: 'true'
  posttoslackApi:
    Type: 'AWS::Serverless::Api'
    Properties:
      StageName: prod
      DefinitionBody:
        swagger: "2.0"
        paths:
          /posttoslack:
            post:
              x-amazon-apigateway-integration:
                uri: 
                  Fn::Sub: arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${slackwebfiltertry.Arn}/invocations
                httpMethod: "POST"
                type: "aws"
```

# 4. 使ってみる（ユーザ向けの手順ってところ）
はい、かんたーーーん！

## (1) スタックの作成
CloudFormationから、[スタックの作成]→[新しいリソースを使用(標準)]を選択する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/384185/71dcf187-88dd-4d7a-0b5a-73a21bf9d4ee.png)

## (2) テンプレートを選択する
S3にぶち込んでるテンプレートのURLを指定する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/384185/257b2f3a-bc06-1a1c-3f9d-42cc7d5c7934.png)

## (3) 名前を記入する
スタックの名前とそこから作成されるLambdaの関数名をそれぞれ記入する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/384185/65bfda54-34a7-acb5-90b7-0f354be659a7.png)

## (4) スタックオプション
このスタックはSlack関連で使ってるってのを分かりやすくするため、タグ付けしておく。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/384185/8e7402e5-5e50-9ac2-0b32-f8b98af369b1.png)

## (5) すべてにチェック
すべてチェックをつけて、スタックの作成を実行する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/384185/8f9aabdc-8a9f-90dc-ff7e-309c69ecc08c.png)

## (6) 終わったら確認
[スタックの情報]でステータスが`CREATE_COMPLETE`になってることを確認する。
※失敗すると`ROLLBACK_COMPLETE`になる。

作成したLambdaやAPIGWは[リソース]から参照できる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/384185/90de2943-48bc-6fee-1d2d-8b45714017bd.png)

また、[テンプレート]から、SAMと、処理されたテンプレートも見れるため、詳細を知りたい場合は結構参考になる。
いや、分からにくいって場合は、[デザイナーで表示]を選択すると、
こんな感じにグラフィカルに表現してくれる。うーん、優しい。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/384185/d9c71d43-1488-fcae-8e98-24761657fd87.png)

## (7) 作成されたLambda修正
勿論テンプレベースなので、Lambdaのコードなどを自分用に修正していく。
これで終わり。


# 5. まとめ
こういった情報はWebに沢山あるし、GUIでほぼ出来ちゃうので仕組み作りも楽しめる。
もっとAWSと仲良くなる旅にでよう、かな。