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

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

<!-- wp:paragraph -->
<p>この方法はAWSのサポートでもありましたので、今回はそれを試してみます。</p>

<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p><a href="https://aws.amazon.com/jp/premiumsupport/knowledge-center/lambda-sns-ses-dynamodb/">https://aws.amazon.com/jp/premiumsupport/knowledge-center/lambda-sns-ses-dynamodb/</a></p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>前提として、AmazonSESに関しては既に設定済としています。</p>
<!-- /wp:paragraph -->
こちら、[ブログ](https://ksysnote.com/uncategorized/aws-ses-sns-lambda-dynamodb/)でも公開しております。

<!-- wp:heading -->
<h2>DynamoDBの設定</h2>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>まずは、DynamoDBのコンソールからテーブルを「SESNotifiations」という名前で作成します。</p>
<!-- /wp:paragraph -->

<!-- wp:group -->
<div class="wp-block-group"><div class="wp-block-group__inner-container"><!-- wp:list -->
<ul><li>テーブル名に「<strong>SESNotifications</strong>」</li><li>パーティションキーに「<strong>SESMessageId</strong>」</li><li>ソートキーに「SESMailSendTime」</li></ul>
<!-- /wp:list --></div></div>
<!-- /wp:group -->

<!-- wp:image {"id":474,"sizeSlug":"large"} -->
<figure class="wp-block-image size-large"><img src="https://ksysnote.com/wp-content/uploads/2020/06/image-11-1024x723.png" alt="" class="wp-image-474"/></figure>
<!-- /wp:image -->

<!-- wp:heading -->
<h2>LambdaのIAMロールを設定</h2>
<!-- /wp:heading -->

<!-- wp:heading {"level":3} -->
<h3>IAMロールの作成</h3>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>次にLambda関数に付与する権限を作成するために、IAMコンソールからロールを作成していきます。</p>
<!-- /wp:paragraph -->

<!-- wp:list -->
<ul><li>信頼されたエンティティの種類から「AWSサービス」を選択</li><li>ユースケースの選択から「Lambda」を選択</li></ul>
<!-- /wp:list -->

<!-- wp:image {"id":475,"sizeSlug":"large","className":"is-style-default"} -->
<figure class="wp-block-image size-large is-style-default"><img src="https://ksysnote.com/wp-content/uploads/2020/06/image-1024x833.png" alt="" class="wp-image-475"/></figure>
<!-- /wp:image -->

<!-- wp:heading {"level":3} -->
<h3>Attachアクセス権限ポリシー</h3>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>ロールにアタッチするポリシーを選択します。</p>
<!-- /wp:paragraph -->

<!-- wp:list -->
<ul><li>「AWSLambdaBasicExecutionRole」を選択</li></ul>
<!-- /wp:list -->

<!-- wp:image {"id":481,"sizeSlug":"large","className":"is-style-default"} -->
<figure class="wp-block-image size-large is-style-default"><img src="https://ksysnote.com/wp-content/uploads/2020/06/image-5-1024x463.png" alt="" class="wp-image-481"/></figure>
<!-- /wp:image -->

<!-- wp:heading {"level":3} -->
<h3>タグの追加</h3>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>今回はタグ無しで次へ</p>
<!-- /wp:paragraph -->

<!-- wp:image {"id":482,"sizeSlug":"large","className":"is-style-default"} -->
<figure class="wp-block-image size-large is-style-default"><img src="https://ksysnote.com/wp-content/uploads/2020/06/image-6-1024x452.png" alt="" class="wp-image-482"/></figure>
<!-- /wp:image -->

<!-- wp:heading {"level":3} -->
<h3>確認</h3>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>ロール名を「<strong>lambda-ses-execution</strong>」としてロールを作成します。</p>
<!-- /wp:paragraph -->

<!-- wp:image {"id":478,"sizeSlug":"large","className":"is-style-default"} -->
<figure class="wp-block-image size-large is-style-default"><img src="https://ksysnote.com/wp-content/uploads/2020/06/image-3-1024x831.png" alt="" class="wp-image-478"/></figure>
<!-- /wp:image -->

<!-- wp:heading {"level":3} -->
<h3>インラインポリシーの追加</h3>
<!-- /wp:heading -->

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

<!-- wp:paragraph -->
<p>ロールの一覧から「<strong><strong>lambda-ses-execution</strong></strong>」を選択して、「インラインポリシーの追加」をクリックします。</p>
<!-- /wp:paragraph -->

<!-- wp:image {"id":479,"sizeSlug":"large","className":"is-style-default"} -->
<figure class="wp-block-image size-large is-style-default"><img src="https://ksysnote.com/wp-content/uploads/2020/06/image-4-1024x470.png" alt="" class="wp-image-479"/></figure>
<!-- /wp:image -->

<!-- wp:heading {"level":3} -->
<h3>ポリシーの作成</h3>
<!-- /wp:heading -->

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

<!-- wp:list -->
<ul><li>サービスから「DyanamoDB」を選択</li><li>アクションから「書き込み」の「PutItem」を選択します</li><li>リソースの「ARNの追加」をクリックします。</li><li>「ARNの追加」から先程作成したDynamoDBの「ARN」を設定します。</li></ul>
<!-- /wp:list -->

<!-- wp:image {"id":483,"sizeSlug":"large","className":"is-style-default"} -->
<figure class="wp-block-image size-large is-style-default"><img src="https://ksysnote.com/wp-content/uploads/2020/06/image-7-1024x493.png" alt="" class="wp-image-483"/></figure>
<!-- /wp:image -->

<!-- wp:image {"id":484,"sizeSlug":"large","className":"is-style-default"} -->
<figure class="wp-block-image size-large is-style-default"><img src="https://ksysnote.com/wp-content/uploads/2020/06/image-8-1024x789.png" alt="" class="wp-image-484"/></figure>
<!-- /wp:image -->

<!-- wp:heading -->
<h2>Lambda関数</h2>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>それでは、実際に関数を作成していきます。ここでは、先程IAMで作成したロールを選択します。</p>
<!-- /wp:paragraph -->

<!-- wp:list -->
<ul><li>関数名に「sesnotificationscode」</li><li>ランタイムは「Node.js 12.x」を選択</li><li>実行ロールは、先程作成した「lambda-ses-execution」を選択する</li></ul>
<!-- /wp:list -->

<!-- wp:image {"id":485,"sizeSlug":"large","className":"is-style-default"} -->
<figure class="wp-block-image size-large is-style-default"><img src="https://ksysnote.com/wp-content/uploads/2020/06/image-9-1024x464.png" alt="" class="wp-image-485"/></figure>
<!-- /wp:image -->

<!-- wp:heading {"level":3} -->
<h3>コード</h3>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>SES通知の内容には「mailオブジェクト」「bonceオブジェクト」「deliveryオブジェクト」「complaintオブジェクト」があります。<br>その中身を解析してDynamoDBに保存していきます。</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>SES通知の内容についてもこちらを参照にして下さい。</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p><a href="https://docs.aws.amazon.com/ja_jp/ses/latest/DeveloperGuide/notification-contents.html">https://docs.aws.amazon.com/ja_jp/ses/latest/DeveloperGuide/notification-contents.html</a></p>
<!-- /wp:paragraph -->

<!-- wp:enlighter/codeblock -->
<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">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();
      }
    });
  }
};
</pre>
<!-- /wp:enlighter/codeblock -->

<!-- wp:heading -->
<h2>SNSトピック</h2>
<!-- /wp:heading -->

<!-- wp:heading {"level":3} -->
<h3>SNSトピックの作成</h3>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>続いて、Lambda関数のトリガーとなるSNSトピックを「ses_notifications_repo」という名称で作成していきます。</p>
<!-- /wp:paragraph -->

<!-- wp:image {"id":489,"sizeSlug":"large","className":"is-style-default"} -->
<figure class="wp-block-image size-large is-style-default"><img src="https://ksysnote.com/wp-content/uploads/2020/06/image-10-1024x497.png" alt="" class="wp-image-489"/></figure>
<!-- /wp:image -->

<!-- wp:heading {"level":3} -->
<h3>トリガーの設定</h3>
<!-- /wp:heading -->

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

<!-- wp:image {"id":490,"sizeSlug":"large","className":"is-style-default"} -->
<figure class="wp-block-image size-large is-style-default"><img src="https://ksysnote.com/wp-content/uploads/2020/06/image-12-1024x367.png" alt="" class="wp-image-490"/></figure>
<!-- /wp:image -->

<!-- wp:image {"id":491,"sizeSlug":"large","className":"is-style-default"} -->
<figure class="wp-block-image size-large is-style-default"><img src="https://ksysnote.com/wp-content/uploads/2020/06/image-13-1024x617.png" alt="" class="wp-image-491"/></figure>
<!-- /wp:image -->

<!-- wp:heading {"level":3} -->
<h3>SES Notificationの設定</h3>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>最後にSESからSNSのトピックを設定していきます。</p>
<!-- /wp:paragraph -->

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

<!-- wp:image {"id":492,"sizeSlug":"large","className":"is-style-default"} -->
<figure class="wp-block-image size-large is-style-default"><img src="https://ksysnote.com/wp-content/uploads/2020/06/image-14-1024x987.png" alt="" class="wp-image-492"/></figure>
<!-- /wp:image -->

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

<!-- wp:heading -->
<h2>テスト</h2>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>「Send Test Email」からテスト配信をして、DynamoDBに保存されるか確認していきます。</p>
<!-- /wp:paragraph -->

<!-- wp:image {"id":493,"sizeSlug":"large","className":"is-style-default"} -->
<figure class="wp-block-image size-large is-style-default"><img src="https://ksysnote.com/wp-content/uploads/2020/06/image-15-1024x672.png" alt="" class="wp-image-493"/></figure>
<!-- /wp:image -->

<!-- wp:paragraph -->
<p>toに宛先を指定すると確認することができます。</p>
<!-- /wp:paragraph -->

<!-- wp:list -->
<ul><li>正常な配信の確認・・・success@simulator.amazonses.com</li><li>バウンス　　　　・・・bounce@simulator.amazonses.com</li><li>苦情　　　　　　・・・complaint@simulator.amazonses.com</li></ul>
<!-- /wp:list -->

<!-- wp:paragraph -->
<p><a href="https://docs.aws.amazon.com/ja_jp/ses/latest/DeveloperGuide/send-email-simulator.html">https://docs.aws.amazon.com/ja_jp/ses/latest/DeveloperGuide/send-email-simulator.html</a></p>
<!-- /wp:paragraph -->