AWS Lambda を CloudFormation でサクッと作って awscli から実行する

# 概要

## 要旨
CloudFormation で Lambda を作って、Lambda を awscli から実行(invoke)する。

## やりたいこと
- Lambda Function を CloudFormation で作成、削除したい
- CloudFormation のテンプレートに関数を埋め込んで一つのファイルだけで管理したい
    - pros: テンプレートに埋め込むとs3へアップロードしておく等の事前準備が不要になる
    - cons: 標準モジュール以外を使用する場合はテンプレート埋め込みの方法は使えない
- awscli から lambda を実行できれば十分
    - HTTP公開は認証やDDoS対策が面倒
    - HTTP公開やイベント登録はしない
        - HTTP公開はAPIの設定が必要になる

# 作業

## 前提
- awscliがインストールされており、認証もされている
- 以下のロールを持っている（もっと削れるかも）
    - AWSLambdaBasicExecutionRole
    - AWSLambdaFullAccess
- Lambda内で標準モジュールしか使わない
- 下記サンプルのランタイムは Python 3.7

## ローカルファイルの用意
`lambda.template.yaml`を以下をコピペしてローカルに作りましょう。

### lambda.template.yaml
```yaml:lambda.template.yaml
AWSTemplateFormatVersion: 2010-09-09
Description: access test
Resources:
  AccessTestLambda:
    Type: 'AWS::Lambda::Function'
    Properties:
      Code:
        ZipFile: |
          import json
          def lambda_handler(event, context):
              return {
                  'msg_const': "Hello, World !!",
                  'msg_from': event["msg"],
              }
      Description: access test lambda
      FunctionName: accessTest
      Handler: index.lambda_handler
      Role: !GetAtt 
        - LambdaExecutionRole
        - Arn
      Runtime: python3.7
  LambdaExecutionRole:
    Type: 'AWS::IAM::Role'
    Properties:
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Principal:
              Service:
                - lambda.amazonaws.com
            Action:
              - 'sts:AssumeRole'
      ManagedPolicyArns:
        - "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
  AccessTestLogGroup:
    Type: 'AWS::Logs::LogGroup'
    Properties:
      LogGroupName: /aws/lambda/accessTest
      RetentionInDays: 14

```


### index.py
上記 yaml で CloudFormation のスタックを作ると、 AWS 内に以下の Python ファイルができます。
（yamlファイルだとpython部分のシンタックスハイライトがされないのでここに記載しただけで、作成する必要はありません）

```python:index.py
import json
def lambda_handler(event, context):
    return {
        'msg_const': "Hello, World !!",
        'msg_from': event["msg"],
    }
```

## CloudFormationでデプロイ


### スタック作成
```bash
$ ls
lambda.template.yaml
$ aws cloudformation create-stack --template-body file://lambda.template.yaml --capabilities CAPABILITY_IAM --stack-name access-test-lambda
{
    "StackId": "arn:aws:cloudformation:ap-northeast-1:000000000000:stack/access-test-lambda/00000000-0000-0000-0000-000000000000"
}
```

以下のリソースが作成されます。
- Lambda Function
- ロール
- ロググループ

#### できたか確認
`"StackStatus": "CREATE_COMPLETE"`になっていれば作成できています。

```bash
$ aws cloudformation describe-stacks --stack-name access-test-lambda
{
    "Stacks": [
        {
            "StackId": "arn:aws:cloudformation:ap-northeast-1:000000000000:stack/access-test-lambda/00000000-0000-0000-0000-000000000000",
            "DriftInformation": {
                "StackDriftStatus": "NOT_CHECKED"
            },
            "Description": "access test",
            "Tags": [],
            "EnableTerminationProtection": false,
            "CreationTime": "2019-12-31T23:59:59.999Z",
            "Capabilities": [
                "CAPABILITY_IAM"
            ],
            "StackName": "access-test-lambda",
            "NotificationARNs": [],
            "StackStatus": "CREATE_COMPLETE",
            "DisableRollback": false,
            "RollbackConfiguration": {}
        }
    ]
}
```

### スタック削除
```bash
$ aws cloudformation delete-stack --stack-name access-test-lambda
```


## awscli から Lambda実行

`aws lambda invoke`で同期実行します。
`result.json`に結果が吐かれます。

```bash
$ echo "存在するドメイン"
$ aws lambda invoke --function-name "accessTest" --payload '{ "msg": "https://example.com" }' result.json
{
    "ExecutedVersion": "$LATEST",
    "StatusCode": 200
}
$ cat result.json
{"msg_const": "Hello, World !!", "msg_from": "https://example.com"}
```