CloudWatch Logsサブスクリプションフィルタを使ってLambdaを動かす

# subscriptionFilter
CloudWatch Logsに入ってきたデータをイベントドリブンに処理が可能なものです。
**各ロググループに１つのみ適応可能という制限があります**

subscriptionFilterを使ってLambdaを起動する方法を説明します。

**なお、subscriptionFilterの設定はマネコンではできないはずです**

## 設定
前提として、動かすLambdaはできているものとします。
設定はCFｎを用いて行います

対象のLambda名：　`sample-lambda`
対象のロググループ名： `sample-log`

```yaml:nested-template.yaml
AWSTemplateFormatVersion: "2010-09-09"
Parameters:
  SourceLogGroup:
    Description: "CWL log group name"
    Type: "String"
  LambdaFunctionName:
    Description: "Lambda function name"
    Type: "String"
  filterPattern:
    Description: "CWL filter pattern"
    Type: "String"
Resources:
  subscriptionFilter:
    Type: "AWS::Logs::SubscriptionFilter"
    Properties:
      LogGroupName: !Ref SourceLogGroup
      FilterPattern: !Ref filterPattern
      DestinationArn: !Sub "arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:${LambdaFunctionName}"
```

設定を行うのは上記のテンプレートのとおりです。

```yaml:subscription-filter-root-stack.yaml
AWSTemplateFormatVersion: "2010-09-09"
Description: "root stack for cloudwatch logs subscription filter template."
Mappings:
  ResourceMapping:
    LambdaFunction:
      name: "sample-lambda"
    clpudwatchLogsFilterPattern:
      all: ""
      error: "error"
Resources:
  CWLSubscriptionFilterStack:
    Type: "AWS::CloudFormation::Stack"
    Properties:
      TemplateURL: <nested-template.yaml への相対パス>
      Parameters:
        SourceLogGroup: "sample-log"
        LambdaFunctionName: !FindInMap [ ResourceMapping, LambdaFunction, name ]
        filterPattern: !FindInMap [ ResourceMapping, clpudwatchLogsFilterPattern, default ]
```

必要なロググループ分リソースを追加していけば大丈夫です。

## デプロイ
aws cli　を用いてデプロイします。設定は終わっているものとします。
ネストしたテンプレートはS3に上がっている必要があるので先にpackage化します。

```shell:command
aws cloudformation package \
    --template-file ./ubscription-filter-root-stack.yaml \ #　２つ目に書いたテンプレートへのパス
    --s3-bucket ${S3_BUCKET_NAME} \　# テンプレートを格納するS3（CWLと同一リージョンのS3を使うこと）
    --s3-prefix ${REPOSITORY_NAME} \　# S3に格納する際のプレフィックスを指定
    --output-template-file ./bscription-filter-root-stack-package.yaml　 #　package化してデプロイに使用するテンプレートの出力先

```

デプロイ

```shell:command
aws cloudformation deploy \
    --s3-bucket ${S3_BUCKET_NAME} \　# テンプレートを格納するS3
    --template-file ./bscription-filter-root-stack-package.yaml \　 #　package化してデプロイに使用するテンプレート
    --stack-name ${STACK_NAME} \ # デプロイするCFｎスタックの名称
    --capabilities CAPABILITY_NAMED_IAM \ # 固定値だと思っていいです
    --role-arn ${ROLE_ARN} # CFnを動かす権限を持ったロールを指定
```

# 注意

CWLのsubscriptionFilterからLambdaを動かす際にはLambdaに特殊な権限が必要です。
権限が無い状態でこのCFｎを動かすとエラーでロールバックします
権限の設定はCLIで行います

```shell:command
aws lambda add-permission \
    --function-name "sample-lambda" \
    --statement-id "sample-log-sample-lambda" \
    --principal "logs.<region>.amazonaws.com" \
    --action "lambda:InvokeFunction" \
    --source-arn "arn:aws:logs:<region>:<account-id>:log-group:sample-log:*" \
    --source-account "<account-id>"
```

<>で囲われてる部分は直してください。

なお、上記commandを打っていない状態であれば下記のCFｎを適応することでデプロイが可能

```yaml:template.yaml
AWSTemplateFormatVersion: "2010-09-09"
Parameters:
  SourceLogGroup:
    Description: "CWL log group name"
    Type: "String"
  LambdaFunctionName:
    Type: "String"
  filterPattern:
    Type: "String"
Resources:
  subscriptionFilter:
    DependsOn:
      - LambdaInvokePermission
    Type: "AWS::Logs::SubscriptionFilter"
    Properties:
      LogGroupName: !Ref SourceLogGroup
      FilterPattern: !Ref filterPattern
      DestinationArn: !Sub "arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:${LambdaFunctionName}"
  LambdaInvokePermission:
    Type: 'AWS::Lambda::Permission'
    Properties:
      FunctionName: !Ref LambdaFunctionName
      Action: "lambda:InvokeFunction"
      Principal: !Sub "logs.amazonaws.com"
      SourceAccount: !Ref AWS::AccountId
      SourceArn: !Sub "arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:${SourceLogGroup}:*"
```

上記テンプレートはCLIを使ってパーミッション入っていると逆に落ちます。