AWS SESバウンス率上昇警告への対応

# はじめに
SESのバウンス率が上昇すると、AWSから警告メールが送られてきます。
放置したままにすると、`SES`の機能が利用停止になるので気を付けましょう。
恥ずかしながら警告メールを受け取ったので対応方法を残しておこうと思います。
きちんと対策しておくことに越したことはないので、
AWSの`SES(Simple Email Serve)`を利用する前に気を付けることは下記を参照してください。
https://qiita.com/uzresk/items/bbca7d5acc6c02534682

# 警告メール
下記のメールを受け取ったらピンチです。

```
Hello,

There's an issue related to your Amazon SES account that requires your attention. This message contains more information about these issues, and some information that might help you resolve them.

We only change your sending status in order to protect your reputation as a sender, and to ensure that other SES customers' ability to send email isn't impacted. We may pause your account's ability to send email so that you have time to address these issues. When you fix these issues, we'll restore your account's ability to send email.

以下省略

```

簡単にメールの内容をまとめるとこんな感じです。

- バウンス率が高いのでステータスをレビュー中にします。
- 次の8万通のメール送信でバウンス率が改善しない、またはこのメールへ返信しない場合`SES`の機能に制限をかけます。
- バウンス率上昇の原因、原因への対応内容、その対応によって同様の問題が起きなくなる理由を書いて返信してください。

# 原因
今回の原因はDBに登録していたメールアドレスが全て`hoge@test.com`みたいになっていたので発生してしまいました。
またCloudwatchでの監視も行っていなかったので、テストメールが送信され続けてバウンス率が上昇しても気づきませんでした。

# 対応
ここからは実際に対応を行っていきます。主に以下の3つです。

- DBに登録されているテストメールをきちんと届くメールアドレスに変更する。
- `CloudWatchAlarm`を設定してバウンス率の上昇を監視する。
- AWSのSESチームにメールを返信する。

他の環境にもすぐに適用できるように2個目は`cloudformation`で記述しました。

```cloudWatchAlarm.yaml
AWSTemplateFormatVersion: '2010-09-09'

Parameters:
  ServiceName:
    Description: "input your ServiceName."
    Type: String
    Default: "app"
  
  Endpoint:
    Description: "input your sns topic to mail"
    Type: String
    Default: "test@test.com（ちゃんと届くメールアドレス）"

Resources:
  # ------------------------------------------------------------#
  #  SES Bounce Number Alart SNS Topic
  # ------------------------------------------------------------#
  SesSnsTopic:
    Type: AWS::SNS::Topic
    Properties:
      DisplayName: !Sub ${ServiceName}-SES-Alart-Topic
      TopicName: !Sub ${ServiceName}-SES-Alart-Topic
    
  SnsSubscription:
    Type: AWS::SNS::Subscription
    Properties:
      Protocol: email
      Endpoint: !Ref Endpoint
      TopicArn: !Ref SesSnsTopic
  # ------------------------------------------------------------#
  #  SES Bounce Number Alart
  # ------------------------------------------------------------#
  BounceNumberAlarm:
    Type: AWS::CloudWatch::Alarm
    Properties:
      AlarmActions: 
        - !Ref SesSnsTopic
      AlarmDescription: !Sub ${ServiceName}-SES-Bounce-Number-Alart
      AlarmName: !Sub ${ServiceName}-SES-Bounce-Number-Alart
      ComparisonOperator: GreaterThanOrEqualToThreshold
      EvaluationPeriods: 1
      Period: 300
      Threshold: 1 #1通でもバウンスメールがあったら通知
      MetricName: Bounce
      Statistic: Average
      Namespace: AWS/SES

  # ------------------------------------------------------------#
  #  SES Bounce Rate Reputation Alart
  # ------------------------------------------------------------#
  BounceRateAlarm:
    Type: AWS::CloudWatch::Alarm
    Properties:
      AlarmActions: 
        - !Ref SesSnsTopic
      AlarmDescription: !Sub ${ServiceName}-SES-Bounce-Rate-Alart
      AlarmName: !Sub ${ServiceName}-SES-Bounce-Rate-Alart
      ComparisonOperator: GreaterThanOrEqualToThreshold
      EvaluationPeriods: 1
      Period: 900
      Threshold: 0.03 #バウンス率が3%以上になったら通知。5%未満をAWSは推奨。
      MetricName: Reputation.BounceRate
      Statistic: Average
      Namespace: AWS/SES

  # ------------------------------------------------------------#
  #  SES Complaint Rate Reputation Alart
  # ------------------------------------------------------------#
  ComplaintRateAlarm:
    Type: AWS::CloudWatch::Alarm
    Properties:
      AlarmActions: 
        - !Ref SesSnsTopic
      AlarmDescription: !Sub ${ServiceName}-SES-Complaint-Rate-Alart
      AlarmName: !Sub ${ServiceName}-SES-Complaint-Rate-Alart
      ComparisonOperator: GreaterThanOrEqualToThreshold
      EvaluationPeriods: 1
      Period: 900
      Threshold: 0.005 #一応、苦情率が0.5%以上になったら通知。1%未満をAWSは推奨。
      MetricName: Reputation.ComplaintRate
      Statistic: Average
      Namespace: AWS/SES
```
これでバウンスが発生したときは通知先に登録したメールアドレスに内容が送られてくるようになります。（AWSから登録したメールアドレスに検証リンクが送られてくるので押下して検証済みにする）

対応が終わったら、下記の3点について英語で内容をまとめたうえで、警告メール下部に記載されている返信先にメールを送信します。
全部Google翻訳を活用して英語で返信しました。

```
*       What caused your high bounce rate?
*       What changes have you made in your email-sending systems or processes?
*       How do these changes ensure that the issue won't occur again in the future?
*       
```

これで次の日には、警告状態を解除してくれました。（その旨のメールが返信されます）

# おまけ
バウンスが発生したメールアドレスとかの情報を残しておきたいと思ったので、`DynamoDB`にバウンス情報を保持するようなLambdaを作成しました。
構成としてはこんな感じです。
SES→SNSトピック→Lambda→DynamoDB
SESに`Bounce Notifications SNS Topic`を設定するところがあるのでそれをLambdaのイベントトリガーにする感じです。

```index.js

const AWS = require('aws-sdk');
const dynamo = new AWS.DynamoDB();

exports.handler = async (event, context) => {
    
    // SNS通知でどんなリクエストが入ってくるのか確認
    // console.log(JSON.stringify(event));
    // console.log(event.Records[0].Sns);

    const getStringMessage = event.Records;
    const promises = getStringMessage.map(record => {
        const date = new Date();
        const createDateUnix = date.getTime();
        const getObjectMessage = JSON.parse(record.Sns.Message);
        const putParams = {
            TableName: "DynamoDBのテーブル名",
            Item: {
                // DynamoDBのパーティションキー（DynamoDBの設計は適当でごめんなさい）
                notificationType: {"S": `${getObjectMessage.notificationType}`},
                // DynamoDBのソートキー（DynamoDBの設計は適当でごめんなさい）
                timestamp: {"N": `${date.getTime()}`},
                // とりあえずもらった内容をそのまんま放り込む。内容はこちらを参照（https://docs.aws.amazon.com/ja_jp/ses/latest/DeveloperGuide/notification-examples.html#notification-examples-bounce）
                body: {"S": JSON.stringify(getObjectMessage)}
            }
        }
        
        return dynamo.putItem(putParams).promise();
    });
    
    await Promise.all(promises)
    .then(results => {
        console.log(results);
    })
    .catch(reject => {
       console.log(reject); 
    });
    
};
    
```
反省します。
以上です。