【AWS】CloudFormationでSQS キューを作成する

# 目的
`amplify push`でSQS　キューを作成します。

# カスタムリソースの作成
ルートディレクターで下記のコマンドでリソースを作成します。
テンプレートファイルは`amplify/backend/custom/<リソース名>/<リソース名>-cloudformation-template.json`に用意されます。


- Custom作成
```sh
% amplify add custom
✔ How do you want to define this custom resource? · AWS CloudFormation
✔ Provide a name for your custom resource · testSqs
✔ Do you want to access Amplify generated resources in your custom CloudFormation file? (y/N) · no
✅ Created skeleton CloudFormation stack in amplify/backend/custom/testSqs directory
✔ Do you want to edit the CloudFormation stack now? (Y/n) · no
```

下記のテンプレートが作成されます。
/amplify/backend/custom/testSqs/testSqs-cloudformation-template.json
```json
{
    "AWSTemplateFormatVersion": "2010-09-09",
    "Parameters": {
      "env": {
        "Type": "String"
      }
    },
    "Resources": {
      // Define your AWS Resources here -  https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/gettingstarted.templatebasics.html
    },
    "Outputs": {
       // Define your Stack Outputs here - https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/gettingstarted.templatebasics.html
    }
}
```

# 条件（Conditions）
```json
  "Conditions": {
    "ShouldNotCreateEnvResources": {
      "Fn::Equals": [
        {
          "Ref": "env"
        },
        "NONE"
      ]
    }
  },
```
環境ごとでソースを管理したい場合、`cloudformation`に上記の条件を追加します。
`env`は`NONE`の場合、リソースを作成しない意味です。

# リソース（Resources）
```json
{
  "Type" : "AWS::SQS::Queue",
  "Properties" : {
      "ContentBasedDeduplication" : Boolean,
      "DeduplicationScope" : String,
      "DelaySeconds" : Integer,
      "FifoQueue" : Boolean,
      "FifoThroughputLimit" : String,
      "KmsDataKeyReusePeriodSeconds" : Integer,
      "KmsMasterKeyId" : String,
      "MaximumMessageSize" : Integer,
      "MessageRetentionPeriod" : Integer,
      "QueueName" : String,
      "ReceiveMessageWaitTimeSeconds" : Integer,
      "RedriveAllowPolicy" : Json,
      "RedrivePolicy" : Json,
      "SqsManagedSseEnabled" : Boolean,
      "Tags" : [ Tag, ... ],
      "VisibilityTimeout" : Integer
    }
}

```
リソースで上記のプロパティを宣言することができます。
## Queue
1. Type: ***AWS::SQS::Queue***
1. Properties
    1. VisibilityTimeout
    メッセージがキューから配信された後、メッセージが利用できなくなる時間の長さ。これにより、他のコンポーネントが同じメッセージを受信することがブロックされ、最初のコンポーネントにメッセージを処理してキューから削除する時間が与えられます。値は 0 ～ 43,200 秒 (12 時間) である必要があります。値を指定しない場合、AWS CloudFormation はデフォルト値の 30 秒を使用します。    
    1. KmsMasterKeyId
    Amazon SQS の AWS Key Management Service (KMS)、またはカスタム KMS の ID。Amazon SQS に AWS 管理の KMS を使用するには、(デフォルト) エイリアス ARN、エイリアス名 (例: alias/aws/sqs)
    1. QueueName
    キュー名
    1. FifoQueue
    true に設定すると、FIFO キューが作成されます。このプロパティを指定しない場合、Amazon SQS は標準キューを作成します

:::note info
関数がレコードの各バッチを処理する時間を取るため、ソースキューの可視性タイムアウトは、関数に設定したタイムアウトの少なくとも 6 倍に設定してください。追加の時間は、関数が前のバッチの処理中にスロットリングされた場合に、Lambda が再試行することを可能にします。
:::

:::note warn
Lambdaをトリガーされてない場合、SQSメッセージの処理終わった後、削除する必要があります。また、**VisibilityTimeout**はメッセージ処理の必要時間以上に設定してください。受信から削除までの時間がかかる場合、それ以上のVisibilityTimeoutが必要です。そうしないと、SQSエラーが発生します。
:::
## RedrivePolicy
1. deadLetterTargetArn
maxReceiveCountの値を超えた後に Amazon SQS がメッセージを移動する配信不能キューの Amazon リソースネーム (ARN)
1. maxReceiveCount
メッセージがデッドレターキューに送られる前にソースキューで受信できる最大回数を定義します。

## サンプル
:::note
alias/SQS-Keyで暗号化するキューを作成し、タイムアウトの時間を360秒で、キューネームは`Fn::If`関数で宣言している。`ShouldNotCreateEnvResources`の条件(envはNONE)がTRUEの場合、キューの名前は**testSqs**、FALSE　の場合、名前は**testSqs-環境名**となる。

DeadLetterQueueは`Fn::GetAtt`関数を使用して`DeadLetterQueue`リソースの`Arn`属性を取得しています。

:::

```json
"Resources": {
    "Queue": {
      "Type": "AWS::SQS::Queue",
      "Properties": {
        "VisibilityTimeout": 360,
        "KmsMasterKeyId": "alias/SQS-Key",
        "QueueName": {
          "Fn::If": [
            "ShouldNotCreateEnvResources",
            "testSqs",
            {
              "Fn::Join": [
                "",
                [
                  "testSqs",
                  "-",
                  {
                    "Ref": "env"
                  }
                ]
              ]
            }
          ]
        },
        "RedrivePolicy": {
          "deadLetterTargetArn": {
            "Fn::GetAtt": [
              "DeadLetterQueue",
              "Arn"
            ]
          },
          "maxReceiveCount": 3
        }
      }
    },
    "DeadLetterQueue": {
      "Type": "AWS::SQS::Queue",
      "Properties": {
        "KmsMasterKeyId": "alias/SQS-Key",
        "QueueName": {
          "Fn::If": [
            "ShouldNotCreateEnvResources",
            "testSqsDeadLetterQueue",
            {
              "Fn::Join": [
                "",
                [
                  "testSqsDeadLetterQueue",
                  "-",
                  {
                    "Ref": "env"
                  }
                ]
              ]
            }
          ]
        }
      }
    }
  },
```

# Outputs
Outputsで出力するキーを宣言できます。
その値はAWS Cloudformationのコンソール画面で確認できます。

```json
  "Outputs": {
    "Url": {
      "Value": {
        "Ref": "Queue"
      }
    },
    "Arn": {
      "Value": {
        "Fn::GetAtt": [
          "Queue",
          "Arn"
        ]
      }
    },
    "DLQArn": {
      "Value": {
        "Fn::GetAtt": [
          "DeadLetterQueue",
          "Arn"
        ]
      }
    }
  },
```

# Lambda作成
**SQS**をデプロイしてから、Lambdaの作成となります。
```sh
% amplify add function
? Select which capability you want to add: Lambda function (serverless function)
? Provide an AWS Lambda function name: testFunction
? Choose the runtime that you want to use: NodeJS
? Choose the function template that you want to use: Hello World

Available advanced settings:
- Resource access permissions
- Scheduled recurring invocation
- Lambda layers configuration
- Environment variables configuration
- Secret values configuration

? Do you want to configure advanced settings? No
? Do you want to edit the local lambda function now? No
Successfully added resource testFunction locally.
```

## 権限付与
もしSQSでLambdaの環境変数などをアクセスしたい場合、権限付与が必要です。
下記２つのコマンドを活用しましよう。
1. amplify update function
1. amplify update custom
```sh
amplify update custom  
✔ Select the custom resource to update · testSqs
✔ Do you want to access Amplify generated resources in your custom CloudFormation file? (Y/n) · yes
? Select the categories you want this custom resource to have access to. function
? Function has 25 resources in this project. Select the one you would like your custom resource to access testF
✔ Do you want to edit the CloudFormation stack now? (Y/n) · no
```

上記の権限を付与すること、Lambdaの`Outputs`で定義した属性はSQSの`Parameters`に持って来られます。

### Lambda関数のcloudformation抜粋

```json
  ...
  "Outputs": {
    "Url": {
      "Value": {
        "Ref": "Queue"
      }
    },
    "Arn": {
      "Value": {
        "Fn::GetAtt": [
          "Queue",
          "Arn"
        ]
      }
    },
    "DLQArn": {
      "Value": {
        "Fn::GetAtt": [
          "DeadLetterQueue",
          "Arn"
        ]
      }
    }
  },
  ...
```

### SQS cloudformationの抜粋
```json
  "Parameters": {
    "env": {
      "Type": "String"
    },
    "functiontestSqsName": {
      "Type": "String",
      "Description": "Input parameter describing Name attribute for function/testSqs resource"
    },
    "functiontestSqsArn": {
      "Type": "String",
      "Description": "Input parameter describing Arn attribute for function/testSqs resource"
    },
    "functiontestSqsRegion": {
      "Type": "String",
      "Description": "Input parameter describing Region attribute for function/testSqs resource"
    }
  }
```

### EventSourceMapping
```json
"MyEventSourceMapping": {
      "Type": "AWS::Lambda::EventSourceMapping",
      "Properties": {
        "EventSourceArn": {
          "Fn::GetAtt": ["Queue", "Arn"]
        },
        "FunctionName": { "Ref": "functiontestSqsArn" },
        "BatchSize": 5
      }
    }
```

- BatchSize
 Lambda がキューから取得して関数に送信する、各バッチのレコードの最大数。Lambda は、同期呼び出しのペイロード制限 (6 MB) まで、バッチ内のすべてのレコードを 1 回の呼び出しで関数に渡します。
SQS – デフォルトは 10。標準キューの場合、最大 10,000 レコードまで可能です。FIFO キューの場合、最大値は 10 です。バッチサイズが 10 を超える場合は、バッチウィンドウ (MaximumBatchingWindowInSeconds) も 1 秒以上に設定する必要があります。

- MaximumBatchingWindowInSeconds
関数を呼び出すまでのレコード収集の最大時間 (秒) です。これが適用されるのは標準キューのみです。
# 参考
https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-sqs-queue.html

https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference.html

https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/aws-resource-lambda-eventsourcemapping.html

https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/with-sqs.html