Lambda 関数を使用して、Amazon SNS 経由で送信された Amazon SES 通知を DynamoDB に保存する方法を教えてください。

最終更新日: 2021 年 2 月 22 日

Amazon Simple Email Service (Amazon SES) で送信されてくる E メールに関する通知を受け取るために、Amazon Simple Notification Service (Amazon SNS) を使っています。AWS Lambda 関数を使用して、このような通知を Amazon DynamoDB テーブルに保存する方法を教えてください。

解決方法

注: 次の Lambda 関数の例は、メーリングリストをホストする場所に基づいて CRM などの書き込み先にデータを書き込む際のテンプレートとして使用できます。

まだ設定していない場合は、Amazon SES からの通知を受信するように Amazon SNS トピックが設定された Amazon SES アカウント (E メールまたはドメイン) をセットアップします。詳細については、「Amazon SNS を使用して Amazon SES 通知を受信する」を参照してください。

次に、以下を実行します。

DynamoDB テーブルを作成する

1.    DynamoDB で、次の属性を持つテーブルを作成します。
[テーブル名] には、「SESNotifications」と入力します。
[プライマリパーティションキー] には、「SESMessageId」と入力します。
プライマリ [ソートキー] には、「SnsPublishTime」と入力します。

2.    次の表の説明に従って、セカンダリインデックスを設定します。この設定により、Lambda はテーブルのクエリを実行し、Amazon SES レポートを作成できます。

インデックス名 パーティションキー ソートキー
SESMessageType-Index SESMessageType (文字列) SnsPublishTime (文字列)
SESMessageComplaintType-Index SESComplaintFeedbackType (文字列) SnsPublishTime (文字列)

注: 必要に応じて、セカンダリインデックスを追加できます。

DynamoDB におけるテーブル作成の詳細に関しては、Amazon DynamoDB の使用開始を参照してください。

DynamoDB テーブルの呼び出しを許可する Lambda 関数の IAM ロールにアクセス許可を追加します。

以下の手順を実行して、Lambda 関数が DynamoDB: PutItem API を呼び出すことを許可する新しい AWS Identity and Access Management (IAM) ロールを作成します。

注: 異なる Lambda 関数には、新しい IAM ロールを作成して使用するのがベストプラクティスです。複数の関数にわたって、ロールを使いまわすのは避けるようにします。

1.    IAM コンソールの左ナビゲーションペインで、[ロール] を選択します。

2.    [ロールの作成] を選択します。

3.    [Select type of trusted entity] (信頼されたエンティティのタイプの選択) で、[AWS サービス] を選択します。

4.    [ユースケースを選択] で、[Lambda] を選択し、[次へ: アクセス許可] を選択します。

5.    [アクセス許可ポリシーのアタッチ] で、[AWSLambdaBasicExecutionRole] 管理ポリシーの横にあるチェックボックスをオンにします。[次へ: タグ] を選択します。

6.    (オプション) ユースケースのロールに IAM タグを追加します。 詳細については、「IAM リソースのタグ付け」を参照してください。

7.    [次へ: 確認] を選択します。

8.    [ロール名*] に「lambda_ses_execution.」と入力します。

9.    [ロールの作成] を選択します

10.    IAM ロールの画面に戻って、ユーザーが作成したロールを選択します。

11.    [アクセス許可] タブで、[インラインポリシーの追加] を選択します。

12.    [ビジュアルエディタ] タブで、[サービスの選択] をクリックし、

13.    [DynamoDB] を選択します。次に、[アクション] で、検索フィールドに「PutItem」と入力し、表示されるドロップダウンリストで「PutItem」の横にあるチェックボックスをオンにします。

14.    [リソース] で [特定] を選択します。次に、[ARN の追加] を選択し、作成した DynamoDB テーブルの ARN を入力します。

15.    [ポリシーの確認] を選択します。

16.    [名前] に、ポリシーの名前を入力します。次に、[ポリシーの作成] を選択します。

DynamoDB テーブルへのアクセスを許可するインライン IAM ポリシーの例

------------------------IAM Policy Begins---------------------------
{
    "Version": "2012-10-17",
    "Statement": [
         {
            "Sid": "Stmt1428510662000",
            "Effect": "Allow",
            "Action": [
                "DynamoDB:PutItem"
            ],
            "Resource": [
                "arn:aws:DynamoDB:us-east-1:12345678912:table/SESNotifications"
            ]
        }
    ]
}
------------------------IAM Policy Ends-----------------------------

Amazon SES および Amazon SNS 通知を処理する Lambda 関数を作成する

次のサンプルコードを使用して Lambda 関数を作成し、「sesnotificationscode」という名前を付けます。関数を作成するときは、作成した「lambda_ses_execution」ロールを関数に割り当ててください。

Amazon SES および Amazon SNS 通知を処理するための Lambda 関数コードの例

次の Lambda 関数コード例では、3 種類の Amazon SNS 通知をチェックします。また、この関数は、関連付けられた Amazon SES 通知を DynamoDB テーブルに保存します。通知の種類の詳細については、Amazon SES の Amazon SNS 通知の例を参照してください

重要: TableName パラメータ SESNotifications を DynamoDB テーブルの名前に置き換えます。

--------------------------Lambda Code Begins------------------------
console.log("Loading event");

var aws = require("aws-sdk");
var ddb = new aws.DynamoDB({ params: { TableName: "SESNotifications" } });

exports.handler = function (event, context, callback) {
  console.log("Received event:", JSON.stringify(event, null, 2));

  var SnsPublishTime = event.Records[0].Sns.Timestamp;
  var SnsTopicArn = event.Records[0].Sns.TopicArn;
  var SESMessage = event.Records[0].Sns.Message;

  SESMessage = JSON.parse(SESMessage);

  var SESMessageType = SESMessage.notificationType;
  var SESMessageId = SESMessage.mail.messageId;
  var SESDestinationAddress = SESMessage.mail.destination.toString();
  var LambdaReceiveTime = new Date().toString();

  if (SESMessageType == "Bounce") {
    var SESreportingMTA = SESMessage.bounce.reportingMTA;
    var SESbounceSummary = JSON.stringify(SESMessage.bounce.bouncedRecipients);
    var itemParams = {
      Item: {
        SESMessageId: { S: SESMessageId },
        SnsPublishTime: { S: SnsPublishTime },
        SESreportingMTA: { S: SESreportingMTA },
        SESDestinationAddress: { S: SESDestinationAddress },
        SESbounceSummary: { S: SESbounceSummary },
        SESMessageType: { S: SESMessageType },
      },
    };
    ddb.putItem(itemParams, function (err, data) {
      if (err) {
        callback(err)
      } else {
        console.log(data);
        callback(null,'')
      }
    });
  } else if (SESMessageType == "Delivery") {
    var SESsmtpResponse1 = SESMessage.delivery.smtpResponse;
    var SESreportingMTA1 = SESMessage.delivery.reportingMTA;
    var itemParamsdel = {
      Item: {
        SESMessageId: { S: SESMessageId },
        SnsPublishTime: { S: SnsPublishTime },
        SESsmtpResponse: { S: SESsmtpResponse1 },
        SESreportingMTA: { S: SESreportingMTA1 },
        SESDestinationAddress: { S: SESDestinationAddress },
        SESMessageType: { S: SESMessageType },
      },
    };
    ddb.putItem(itemParamsdel, function (err, data) {
      if (err) {
        callback(err)
      } else {
        console.log(data);
        callback(null,'')
      }
    });
  } else if (SESMessageType == "Complaint") {
    var SESComplaintFeedbackType = SESMessage.complaint.complaintFeedbackType;
    var SESFeedbackId = SESMessage.complaint.feedbackId;
    var itemParamscomp = {
      Item: {
        SESMessageId: { S: SESMessageId },
        SnsPublishTime: { S: SnsPublishTime },
        SESComplaintFeedbackType: { S: SESComplaintFeedbackType },
        SESFeedbackId: { S: SESFeedbackId },
        SESDestinationAddress: { S: SESDestinationAddress },
        SESMessageType: { S: SESMessageType },
      },
    };
    ddb.putItem(itemParamscomp, function (err, data) {
      if (err) {
        callback(err)
      } else {
        console.log(data);
        callback(null,'')
      }
    });
  }
};
------------------------Lambda Code Ends----------------------------

Lambda 関数を 1 つ、または複数の Amazon SNS トピックにサブスクライブします。

まだ Amazon SNS トピックを作成していない場合は、少なくとも 1 つの Amazon SNS トピックを作成しその SNS トピックをフィードバック通知に使用するように Amazon SES E メールドメインを設定します。次に、Amazon SNS コンソールまたは Lambda コンソールのいずれかを使用して、次の操作を行います。

Amazon SNS コンソールを使用して Amazon SNS トピックに関数をサブスクライブするには

1.    Amazon SNS コンソールの左のナビゲーションペインで、[トピック] を選択します。次に、バウンス通知のために Amazon SES で使用される SNS トピックを特定します。例えば、ses_notifications_repo という名前の SNS トピックを特定します。

2.    SNS トピックの ARN を選択して、[トピックの詳細] ページを開きます。

3.    [サブスクリプションの作成] を選択します。

4.    [プロトコル] で、[AWS Lambda] を選択します。

5.    [エンドポイント] に、作成した Lambda 関数の ARN を入力します。次に、[サブスクリプションの作成] を選択します。

Lambda コンソールを使用して Amazon SNS トピックに関数をサブスクライブするには

1.    Lambda コンソールで、前に作成した Lambda 関数を選択します。

2.    [設定] ページの [デザイナー] ペインで、[+トリガーを追加] ボタンを選択します。

3.    [トリガーの設定] ドロップダウンリストで、[SNS] を選択します。設定パネルがデザイナーの下に表示されます。

4.    [SNS トピック] ドロップダウンで、関数をサブスクライブする SNS トピックを選択します。次に、[追加] ボタンをクリックします。

この手順を繰り返して、この Lambda 関数をサブスクライブしたい別の通知トピックを追加します。

Lambda 関数を起動するために Amazon SES メッセージを送信して設定をテストする

テストメッセージを送信するには、メールボックスシミュレーターで使用可能なアドレスの 1 つを使用して、ユーザーの配信性能メトリクスに対する悪影響を回避します。詳細については、「メールボックスシミュレーターの使用」を参照してください。

Amazon SES メッセージが送信されたら、SES は SNS トピックに対する通知を発行します。次に、Amazon SNS はこの通知を Lambda に対し、SNS イベントオブジェクトにおける JSON エスケープした SES イベント通知オブジェクトとして配信します。

Lambda コンソールを使用してローカルテスト用のサンプルイベントを作成するには、「Amazon SES が Amazon SNS に公開するイベントデータの例」を参照してください。

SES バウンス、苦情、 配信通知の例については、「Amazon SES が Amazon SNS に公開するイベントデータの例」を参照してください。

DynamoDB からレポートをダウンロードして、Amazon SES 通知を表示する

DynamoDB テーブルの内容をクエリ、ソート、および CSV ファイルとしてダウンロードするには、次の手順を実行します。

1.    DynamoDB コンソールで、[SESNotifications] テーブルを選択します。

2.    [項目] タブを選択します。

3.    [クエリ] または [スキャン] 検索を作成します。詳細については、「データのクエリとスキャンに関するベストプラクティス」を参照してください。

注: DynamoDB テーブルのエクスポートを使用して、Amazon Simple Storage Service (Amazon S3) バケットへのファイルのダウンロードを定期的にスケジュールできます。詳細については、「DynamoDB テーブルデータを Amazon S3 にエクスポートする」を参照してください。