DynamoDBストリームをトリガーにLambdaを実行しSNS通知する

#1.ストリームを有効にしたdynamoDBテーブルを作成する#


テーブル名：dynamodbtesttable
パーティションキー：Username
ソートキー：Timestamp
ストリーム：有効、トリームに書き込まれる情報を項目の新しいイメージと古いイメージの両方（NEW_AND_OLD_IMAGES）にしました。

|StreamViewType| |
|---|---|
|StreamViewType | テーブル内のデータが変更されるたびにストリームに書き込まれる情報を指定します。|
|KEYS_ONLY | 変更された項目のキー属性のみ。|
|NEW_IMAGE | 変更後に表示される項目全体。|
|OLD_IMAGE | 変更前に表示されていた項目全体。|
|NEW_AND_OLD_IMAGES |項目の新しいイメージと古いイメージの両方。|

```
aws dynamodb create-table \
    --table-name dynamodbtesttable \
    --attribute-definitions AttributeName=Username,AttributeType=S AttributeName=Timestamp,AttributeType=S \
    --key-schema AttributeName=Username,KeyType=HASH  AttributeName=Timestamp,KeyType=RANGE \
    --provisioned-throughput ReadCapacityUnits=5,WriteCapacityUnits=5 \
    --stream-specification StreamEnabled=true,StreamViewType=NEW_AND_OLD_IMAGES
```

出力された LatestStreamArn をメモしておく。

`"LatestStreamArn": "arn:aws:dynamodb:ap-northeast-1:012345678912:table/dynamodbtesttable/stream/2019-12-23T05:35:44.887"`



#2.Lambda 実行ロールの作成#

`LambdatestRole`を作成します。


信頼関係

```json:trust-relationship.json
{
   "Version": "2012-10-17",
   "Statement": [
     {
       "Effect": "Allow",
       "Principal": {
         "Service": "lambda.amazonaws.com"
       },
       "Action": "sts:AssumeRole"
     }
   ]
 }
```

ロール

```
aws iam create-role --role-name LambdatestRole \
    --path "/service-role/" \
    --assume-role-policy-document file://trust-relationship.json
```

ポリシー

```json:role-policy.json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": "lambda:InvokeFunction",
            "Resource": "arn:aws:lambda:ap-northeast-1:123456789012:function:publishNewtest*"
        },
        {
            "Effect": "Allow",
            "Action": [
                "logs:CreateLogGroup",
                "logs:CreateLogStream",
                "logs:PutLogEvents"
            ],
            "Resource": "arn:aws:logs:ap-northeast-1:123456789012:*"
        },
        {
            "Effect": "Allow",
            "Action": [
                "dynamodb:DescribeStream",
                "dynamodb:GetRecords",
                "dynamodb:GetShardIterator",
                "dynamodb:ListStreams"
            ],
            "Resource": "arn:aws:dynamodb:ap-northeast-1:123456789012:table/dynamodbtesttable/stream/*"
        },
        {
            "Effect": "Allow",
            "Action": [
                "sns:Publish"
            ],
            "Resource": [
                "*"
            ]
        }
    ]
}
```

ロールにポリシーをアタッチ

```
aws iam put-role-policy --role-name LambdatestRole \
    --policy-name LambdatestRolePolicy \
    --policy-document file://role-policy.json
```



#3.SNSトピックを作成する#

トピック

```
aws sns create-topic --name snstestTopic
```

サブスクリプション

```
aws sns subscribe \
    --topic-arn arn:aws:sns:ap-northeast-1:123456789012:snstestTopic \
    --protocol email \
    --notification-endpoint example@example.com
```    

E メールアドレスに届いた確認メッセージの [サブスクリプションを確認] リンクを選択して、サブスクリプションプロセスを完了します。



#4.Lambda 関数の作成#

コード

```js:publishNewBark.js
'use strict';
var AWS = require("aws-sdk");
var sns = new AWS.SNS();

exports.handler = (event, context, callback) => {

    event.Records.forEach((record) => {
        console.log('Stream record: ', JSON.stringify(record, null, 2));
        
        if (record.eventName == 'INSERT') {
            var who = JSON.stringify(record.dynamodb.NewImage.Username.S);
            var when = JSON.stringify(record.dynamodb.NewImage.Timestamp.S);
            var what = JSON.stringify(record.dynamodb.NewImage.Message.S);
            var params = {
                Subject: 'A new bark from ' + who, 
                Message: 'test user ' + who + ' barked the following at ' + when + ':\n\n ' + what,
                TopicArn: 'arn:aws:sns:ap-northeast-1:accountID:snstestTopic'
            };
            sns.publish(params, function(err, data) {
                if (err) {
                    console.error("Unable to send message. Error JSON:", JSON.stringify(err, null, 2));
                } else {
                    console.log("Results from sending message: ", JSON.stringify(data, null, 2));
                }
            });
        }
    });
    callback(null, `Successfully processed ${event.Records.length} records.`);
```  

zip

``` 
zip publishNewBark.zip publishNewBark.js
``` 

ロールARNを確認。
```
aws iam get-role --role-name LambdatestRole 


"Arn": "arn:aws:iam::123456789012:role/service-role/LambdatestRole "
```

Lambda関数の作成

```
aws lambda create-function \
    --region ap-northeast-1 \
    --function-name publishNewBark \
    --zip-file fileb://publishNewBark.zip \
    --role arn:aws:iam::123456789012:role/service-role/LambdatestRole  \
    --handler publishNewBark.handler \
    --timeout 5 \
    --runtime nodejs10.x
```

テスト用のpayload.jsonを作成

```json:payload.json
{
    "Records": [
        {
            "eventID": "7de3041dd709b024af6f29e4fa13d34c",
            "eventName": "INSERT",
            "eventVersion": "1.1",
            "eventSource": "aws:dynamodb",
            "awsRegion": "ap-northeast-1",
            "dynamodb": {
                "ApproximateCreationDateTime": 1479499740,
                "Keys": {
                    "Timestamp": {
                        "S": "2016-11-18:12:09:36"
                    },
                    "Username": {
                        "S": "naata"
                    }
                },
                "NewImage": {
                    "Timestamp": {
                        "S": "2016-11-18:12:09:36"
                    },
                    "Message": {
                        "S": "This is a bark from the test social network"
                    },
                    "Username": {
                        "S": "naata"
                    }
                },
                "SequenceNumber": "13021600000000001596893679",
                "SizeBytes": 112,
                "StreamViewType": "NEW_IMAGE"
            },
            "eventSourceARN": "arn:aws:dynamodb:ap-northeast-1:123456789012:table/dynamodbtesttable/stream/2019-12-23T05:35:44.887"
        }
    ]
}
```

テストする。

```
$ aws lambda invoke --function-name publishNewBark --payload file://payload.json output.txt
{
    "ExecutedVersion": "$LATEST",
    "StatusCode": 200
}
```

output.txtに出力された内容を確認する。

```output.txt
"Successfully processed 1 records.
```

SNSメール通知されていることも確認。



#5.本番テスト#

dynamodbのArnを確認。

```
aws dynamodb describe-table --table-name dynamodbtesttable

 "LatestStreamArn": "arn:aws:dynamodb:ap-northeast-1:123456789012:table/dynamodbtesttable/stream/2019-12-23T05:35:44.887
```

イベントソースとAWS Lambda関数間のマッピングを作成

```
aws lambda create-event-source-mapping \
    --region ap-northeast-1 \
    --function-name publishNewBark \
    --event-source arn:aws:dynamodb:ap-northeast-1:268546037544:table/dynamodbtesttable/stream/2019-12-23T05:35:44.887 \
    --batch-size 1 \
    --starting-position TRIM_HORIZON
```

dynamodbテーブルに項目を追加。

```
aws dynamodb put-item \
    --table-name dynamodbtesttable \
    --item Username={S="Jane Doe"},Timestamp={S="2016-11-18:14:32:17"},Message={S="Testing...1...2...3"}
```

SNS通知されたことを確認。

おしまい。