Amazon SESの通知結果をAmazon SNS、Lambdaを使用してDynamoDBに保存する

AmazonSESの送信結果を取得するために、今回はAmazonSNSをトリガーとして、LambdaからDyanamoDBに保存する仕組みを作っていきたいと思います。

この方法はAWSのサポートでもありましたので、今回はそれを試してみます。

AWS Lambda 関数を使用してSES を DynamoDB に保存する

前提として、AmazonSESに関しては既に設定済としています。

DynamoDBの設定

まずは、DynamoDBのコンソールからテーブルを「SESNotifiations」という名前で作成します。

  • テーブル名に「SESNotifications
  • パーティションキーに「SESMessageId
  • ソートキーに「SESMailSendTime」

LambdaのIAMロールを設定

IAMロールの作成

次にLambda関数に付与する権限を作成するために、IAMコンソールからロールを作成していきます。

  • 信頼されたエンティティの種類から「AWSサービス」を選択
  • ユースケースの選択から「Lambda」を選択

Attachアクセス権限ポリシー

ロールにアタッチするポリシーを選択します。

  • 「AWSLambdaBasicExecutionRole」を選択

タグの追加

今回はタグ無しで次へ

確認

ロール名を「lambda-ses-execution」としてロールを作成します。

インラインポリシーの追加

作成したロールを選択して、DynamoDBへアクセスできるようにインラインポリシーを追加していきます。

ロールの一覧から「lambda-ses-execution」を選択して、「インラインポリシーの追加」をクリックします。

ポリシーの作成

DynamoDBへの書き込み権限を与えるために「ビジュアルエディタ」から権限を設定していきます。

  • サービスから「DyanamoDB」を選択
  • アクションから「書き込み」の「PutItem」を選択します
  • リソースの「ARNの追加」をクリックします。
  • 「ARNの追加」から先程作成したDynamoDBの「ARN」を設定します。

Lambda関数

それでは、実際に関数を作成していきます。ここでは、先程IAMで作成したロールを選択します。

  • 関数名に「sesnotificationscode」
  • ランタイムは「Node.js 12.x」を選択
  • 実行ロールは、先程作成した「lambda-ses-execution」を選択する

コード

SES通知の内容には「mailオブジェクト」「bonceオブジェクト」「deliveryオブジェクト」「complaintオブジェクト」があります。
その中身を解析してDynamoDBに保存していきます。

SES通知の内容についてもこちらを参照にして下さい。

Amazon SES の Amazon SNS 通知の内容 - Amazon Simple Email Service
JavaScript Object Notation (JSON) 形式で Amazon SNS に発行される Amazon SES 通知の内容を説明します。
console.log('Loading event');
var aws = require('aws-sdk');
var ddb = new aws.DynamoDB({params: {TableName: 'SESNotifications'}});
exports.handler = function(event, context)
{
  console.log('Received event:', JSON.stringify(event, null, 2));

  var SESMessage = event.Records[0].Sns.Message
  SESMessage = JSON.parse(SESMessage);
  
  var SESMessageId = SESMessage.mail.messageId;
  var SESMailSendTime = SESMessage.mail.timestamp;
  var SESMessageType = SESMessage.notificationType;
  var SESMailObject = JSON.stringify(SESMessage.mail);
  var SESMailAddress = SESMessage.mail.destination.toString();
  
  var LambdaReceiveTime = new Date().toString();
  if (SESMessageType == 'Bounce')
  {
    var SESBounceObject = JSON.stringify(SESMessage.bounce);
    var SESbounceType = SESMessage.bounce.bounceType;
    var SESbounceSubType = SESMessage.bounce.bounceSubType;
    var SESBounceFeedbackId = SESMessage.bounce.feedbackId;
    
    var itemParams = {
      Item: {
        SESMessageId: {S: SESMessageId}, 
        SESMailSendTime: {S: SESMailSendTime},
        SESMailAddress: {S: SESMailAddress}, 
        SESMessageType: {S: SESMessageType},
        
        SESBounceObject: {S: SESBounceObject},
        SESbounceType: {S: SESbounceType},
        SESbounceSubType: {S: SESbounceSubType},
        SESBounceFeedbackId: {S: SESBounceFeedbackId}
      }};
    ddb.putItem(itemParams, function(err, data)
    {
      if(err) { context.fail(err)}
      else {
           console.log(data);
           context.succeed();
      }
    });
  }
  else if (SESMessageType == 'Delivery')
  {
    var SESDeliveryObject = JSON.stringify(SESMessage.delivery);
    var SESDeliveryTime = SESMessage.delivery.timestamp;
    var SESProcessingTimeMillis = SESMessage.delivery.processingTimeMillis.toString();
    
    var itemParamsdel = {
      Item: {
        SESMessageId: {S: SESMessageId}, 
        SESMailSendTime: {S: SESMailSendTime}, 
        SESMailAddress: {S: SESMailAddress }, 
        SESMessageType: {S: SESMessageType},
        
        SESDeliveryObject: {S: SESDeliveryObject},
        SESProcessingTimeMillis: {N: SESProcessingTimeMillis},
        SESDeliveryTime: {S: SESDeliveryTime}
    }};
    ddb.putItem(itemParamsdel, function(err, data)
    {
      if(err) { context.fail(err)}
      else {
          console.log(data);
          context.succeed();
      }
    });
  }
  else if (SESMessageType == 'Complaint')
  {
    var SESComplaintObject = JSON.stringify(SESMessage.complaint);
    
    var SESComplaintFeedbackType = SESMessage.complaint.complaintFeedbackType;
    var SESComplaintFeedbackId = SESMessage.complaint.feedbackId;
    var itemParamscomp = {
      Item: {
        SESMessageId: {S: SESMessageId}, 
        SESMailSendTime: {S: SESMailSendTime}, 
        SESComplaintFeedbackType: {S: SESComplaintFeedbackType},
        SESComplaintFeedbackId: {S: SESComplaintFeedbackId},
        SESMailAddress: {S: SESMailAddress }, 
        SESMessageType: {S: SESMessageType},
        
        SESComplaintObject: {S: SESComplaintObject}
      }};
    ddb.putItem(itemParamscomp, function(err, data)
    {
      if(err) { context.fail(err)}
      else {
          console.log(data);
          context.succeed();
      }
    });
  }
};

SNSトピック

SNSトピックの作成

続いて、Lambda関数のトリガーとなるSNSトピックを「ses_notifications_repo」という名称で作成していきます。

トリガーの設定

Lambdaコンソールの「トリガーを追加」から、先程作成したSNSのトピックを選択すると簡単に設定がされます。

SES Notificationの設定

最後にSESからSNSのトピックを設定していきます。

SESコンソールのEamail Addressで今回対象となるメール選択して、NotificationsからSNSトピックの「ses_notifications_repo」を選択します。Bounes、Complaints、Deliveriesのそれぞれで設定します。

これで、SESから送信したメールの通知結果を、SNSがトリガーとなってLambda関数を実行して、DynamoDBに保存する仕組みができました。

テスト

「Send Test Email」からテスト配信をして、DynamoDBに保存されるか確認していきます。

toに宛先を指定すると確認することができます。

  • 正常な配信の確認・・・success@simulator.amazonses.com
  • バウンス    ・・・bounce@simulator.amazonses.com
  • 苦情      ・・・complaint@simulator.amazonses.com
Amazon SES での E メール送信のテスト - Amazon Simple Email Service
さまざまな E メール送信シナリオにアプリケーションがどのように対応するかをテストします。送信クォータまたはバウンスや苦情のメトリクスに影響を与えることはありません。

コメント