SESでバウンスメールを確認する方法をいくつか試してみた

2021.12.15

こんにちは、コンサル部@大阪オフィスのTodaです。

Amazon Simple Email Service(以降SES)は任意のアプリケーションからメール配信をする際、おすすめのサービスになります。
メール配信はエンドユーザ様から指定頂いた、メールアドレスに対しておこないますが、入力ミスによる受信失敗(バウンスメール)、迷惑メール報告(苦情メール)が発生する場合があり管理が必要になります。
今回は、簡易に運営者側でバウンスメールに気づける方法を試してみます。

バウンスメール対策の重要性

SESではサービスを利用して送信したメールの中でバウンスメールと苦情メールの計測をしており、一定以上になった場合、レビュー対象や配信の停止がおこなわれます。処理を維持をするためバウンスメールの対応が必要になります。

■ バウンスに関するよくある質問 から引用 https://docs.aws.amazon.com/ja_jp/ses/latest/DeveloperGuide/faqs-enforcement.html#e-faq-bn

Q4. アカウントのレビューや、送信の一時停止の原因になりえるバウンス率は公開されていますか?
最良の結果を得るには、バウンス率を 2% 未満に維持する必要があります。これより高いバウンス率は、E メールの配信に影響する可能性があります。バウンス率が 5% 以上になると、アカウントはレビュー対象になります。バウンス率が 10% 以上の場合は、高いバウンス率の原因となった問題が解決するまで、以後の E メール送信を一時停止することがあります。

バウンス率の確認方法

バウンス率はSESの画面にて確認が可能です。
AWSマネージメントコンソールにログインをおこないサービスメニューから[SES]を選択します。
左メニューから[Reputation metrics]を選択頂き表示される「Bounce rate」と「Complaint rate」がそれぞれの率になります。
それぞれ Warning と Account at risk にラインが引かれているのがわかります。
詳細はCloudWatchにて確認する事も可能です。

バウンス率の確認方法

試してみること

SESにてバウンスメールを受け付けた場合、フィードバック通知機能によりSNSに連携が可能です。 SNSからはメール通知やLambda,SQS,HTTPなど連携ができるのですが、今回は2点を試します。

  • 1.メール通知にて対応
  • 2.ログ保管にて対応 (Lambda + CloudWatch Log メールアドレスログ保管)

試してみること

事前の条件

SESはドメインと検証用メールアドレスが設定されている状態から設定をおこないます。
SESは送信制限が解除されている状態から設定をおこないます。

共通の設定

各処理で利用するSNSとSESの連携設定をおこないます。

SNSの新規作成

AWSマネージメントコンソールにログインをおこないサービスメニューから[SNS]を選択します。
左メニューから[トピック]を選択して一覧上部に表示される[トピックの作成]をクリックします。

SNSの設定

トピックの設定では下記設定にて新規登録をおこないます。
内容を入力して画面下の[トピックの作成]をクリックします。

  • タイプ:スタンダード
  • 名前:任意の名称 (ses-feedback)

SESの通知設定に移動

サービスメニューから[SES]を選択します。
画面は新インターフェイスにて操作を進めます。
左メニューから[Verified identities]を選択して作成済みの設定からバウンス処理が必要なIdentityを選択して詳細画面を表示します。

SESの通知設定に移動1

詳細画面で[Notifications]タブを選択いただきEmail feedback forwardingの項目がEnabledになっている事を確認して、Feedback notificationsの[Edit]をクリックします。

SESの通知設定に移動2

SESの通知設定

通知設定はBounce / Complaint / Delivery の3点があります。
今回はBounce と Complaint に対して作成したSNSを選択します。

SESの通知設定

上記設定を完了することで共通部分の設定は完了になります。

メール通知の設定

SNSにメール通知用のサブスクリプションを追加します。

サブスクリプションの追加

サービスメニューから[SNS]を選択します。
左メニューから[サブスクリプション]を選択して一覧上部の[サブスクリプションの作成]をクリックします。

サブスクリプションの設定

トピックARNから作成したSNS(ses-feedback)を選択します。
プロトコル選択にてEメールを選択して、エンドポイントに通知先メールアドレスを指定します。
※ 大量にメール配信をしている場合は 通知先メールアドレス は専用の物を作成いただくことをおすすめいたします。
画面下の[サブスクリプションの作成]をクリックします。

通知先メールの確認

登録が完了すると通知先メールアドレスに対して確認用のメールが通知されます。
メール内のリンクをクリックする事で認証が完了します。

通知先メールの確認1

通知先メールの確認2

SNSのサブスクリプション画面上もステータスが確認済みに変更されます。

通知先メールの確認3

メールを受信してみる

SESの検証ツールにてシナリオ:バウンスを設定してテストをしたところ通知先メールアドレスに内容が送信されることを確認しました。
情報はJSON形式で表記されているため少し見にくいですが「emailAddress」の内容がバウンス対象のメールアドレスになります。

メールを受信してみる

上記でメール通知の設定は完了になります。

ログ保管について

Lambda処理を利用してCloudWatch Logにバウンスと苦情のメールアドレスが記録されるようにします。
ログ保管と並行にメール通知の仕組みを使う事も可能です。

Lambda処理の作成

サービスメニューから[Lambda]を選択します。
一覧の上部にある[関数の作成]をクリックします。
[1から作成]にてLambdaの関数名とランタイム(Python3.9)を選択します。
アクセス権限はデフォルトの物を利用します。

プログラム

下記Lambda用のプログラムをコードソース欄に入力します。
プログラムはCloudWatch Logのロググループがない場合新規作成するように作成しています。
[ロググループ名]の箇所は任意の名前に書き換えて下さい。

※プログラムご利用時の注意事項
プログラムは動作保証ができないため、ご利用者様にて評価のうえ、ご利用ください。

import json
import time
import logging
import boto3

LogGroupName = "[ロググループ名]"
logger = logging.getLogger()

def lambda_handler(event, context):
    SnsMessage = event['Records'][0]['Sns']['Message']
    SnsMessage = json.loads(SnsMessage);
    # logger.warn(SnsMessage)
    
    NotificationType = SnsMessage['notificationType']
    SendEmail = SnsMessage['mail']['destination'][0]
    
    # boto3
    client = boto3.client('logs')
    
    # ログ書込
    put_logs(client, LogGroupName, NotificationType, SendEmail)

def put_logs(client, group_name, stream_name, email):
    try:
        log_event = {
            'timestamp': int(time.time()) * 1000,
            'message': email
        }
        
        exist_log_stream = True
        sequence_token   = None

        for loop in range(3):
            break_loop = False
            try:
                if exist_log_stream == False:
                    #初回用 
                    #ロググループ作成
                    try:
                        client.create_log_group(logGroupName=group_name)
                    except client.exceptions.ResourceAlreadyExistsException:
                        pass

                    #ログストリーム作成
                    client.create_log_stream(
                        logGroupName = group_name,
                        logStreamName = stream_name)
                    exist_log_stream = True

                    #ログ書込
                    client.put_log_events(
                        logGroupName = group_name,
                        logStreamName = stream_name,
                        logEvents = [log_event])
                    break_loop = True

                elif sequence_token is None:
                    #ログ書込
                    client.put_log_events(
                        logGroupName = group_name,
                        logStreamName = stream_name,
                        logEvents = [log_event])

                else:
                    #ログ書込 (トークンあり)
                    client.put_log_events(
                        logGroupName = group_name,
                        logStreamName = stream_name,
                        logEvents = [log_event],
                        sequenceToken = sequence_token)
                    break_loop = True
            
            except client.exceptions.ResourceNotFoundException as e:
                exist_log_stream = False

            except client.exceptions.DataAlreadyAcceptedException as e:
                sequence_token = e.response.get('expectedSequenceToken')

            except client.exceptions.InvalidSequenceTokenException as e:
                sequence_token = e.response.get('expectedSequenceToken')

            except Exception as e:
                logger.warn(e)
            
            if break_loop:
                break

    except Exception as e:
        logger.warn(e)

上記にて[Deploy]をおこない保存します。
Deploy後、Lambda関数のARNが必要になるためメモに残します。

権限の修正

Lambda標準のCloudWatch Log以外に書込をする為、権限の調整をおこないます。
Lambda画面上の[設定]タブをクリックして左メニュー[アクセス権限]をクリックします。
表示されるLambdaリンクをクリックしてIAMの設定画面に移動します。

権限の修正

設定されている管理ポリシーを選択してJSON形式の編集をおこないます。

IAMの修正

現状の内容に追記します。
Resourceの表記はお客様の環境により変更が必要です。
ロググループ名はLambda処理に指定した物を入力します。

IAMポリシーの変更

{
    "Version": "2012-10-17",
    "Statement": [
        {
            [Lambdaデフォルトの記載]
        },
        {
            [Lambdaデフォルトの記載]
        },
        {
            "Effect": "Allow",
            "Action": [
                "logs:CreateLogStream",
                "logs:PutLogEvents"
            ],
            "Resource": [
                "arn:aws:logs:[リージョン]:[AWSアカウントNo]:log-group:[ロググループ名]:*"
            ]
        }
    ]
}

設定の保存をおこないます。

サブスクリプションの追加

サービスメニューから[SNS]を選択します。
左メニューから[サブスクリプション]を選択して一覧上部の[サブスクリプションの作成]をクリックします。

サブスクリプションの設定

トピックARNから作成したSNS(ses-feedback)を選択します。
プロトコル選択にてAWS Lambdaを選択して、Lambda関数のARNを指定します。
画面下の[サブスクリプションの作成]をクリックします。

メールを受信してみる

SESの検証ツールにてシナリオ:バウンスを設定してテストをおこないます。
サービスメニューから[CloudWatch]を選択します。
左メニューから[ロググループ]を選択して一覧の中から指定したロググループ名(ses-feedback)を選択します。

ログストリームにはBounce / Complaint / Delivery(任意)を分けて記録するようにしています。
Bounceを開くことで先ほど検証したメールアドレスが保存されていたら処理は正常にできています。

メールを受信してみる1

メールを受信してみる2

SESの検証ツールについて

SESにはメール送信の機能があり、シナリオによりバウンスや苦情メール受け取り時の動作検証をおこなうことができます。
サービスメニューから[SES]を選択いただき、右メニュー[Verified identities] > 対象のSES設定を選択します。
上部の[Send test email]を選択します。

昔はメール送信をするのみでしたがシナリオ設定により下記検証が可能です。
SNS連携処理の検証が大変対応しやすくなっています。

▼ 用意されているシナリオ

  • Successful delivery: 正常配信
  • Bounce: バウンス
  • Complaint: 苦情
  • Recipient address on suppression list:サプレッション対応
  • Automatic response:自動応答
  • Custom: 今までと同じ指定メールアドレスに送信

SESの検証ツールについて

さいごに

今回はSESで送信されたメールでバウンス/苦情になっているメールアドレスを把握する方法についてご案内させていただきました。
少しでもお客様の作りたい物の参考になればと考えております。