Lambda (runtime Node.js 8.10) から SNS へ publish してメール送信する


# 概要
CloudWatch Logs に特定文字列が含まれた場合、メール本文をカスタマイズして送信したいということがある。
Zipファイルをデプロイして Lambda から SNS へ publish してメール送信を試したので備忘録として記録しておく。

# 環境
- Amazon Linux 
- node	v8.15.1
- npm   v6.4.1

# 手順

1.  関数をファイルとして準備する
2. ZIP にする

    ```
    $ cd /home/ec2-user/lambda
    $ npm install aws-sdk
    $ ls -al
    total 20
    drwxrwxr-x 3 ec2-user ec2-user 4096 Mar 4 18:11 .
    drwx------ 10 ec2-user ec2-user 4096 Mar 4 18:11 ..
    -rw-rw-r— 1 ec2-user ec2-user 613 Mar 4 17:57 hello_nodejs.js
    drwxrwxr-x 17 ec2-user ec2-user 4096 Mar 4 18:11 node_modules
    -rw-rw-r— 1 ec2-user ec2-user 3420 Mar 4 18:11 package-lock.json
    
    $ npm install zlib
    npm WARN saveError ENOENT: no such file or directory, open '/home/ec2-user/lambda/package.json'
    npm WARN enoent ENOENT: no such file or directory, open '/home/ec2-user/lambda/package.json'
    npm WARN lambda No description
    npm WARN lambda No repository field.
    npm WARN lambda No README data
    npm WARN lambda No license field.
    + zlib@1.0.5
    added 1 package from 1 contributor and audited 38 packages in 0.9s
    found 0 vulnerabilities
    
    $ zip -r ../hello_nodejs.zip .
    $ zipinfo ../hello_nodejs.zip
    Archive: ../hello_nodejs.zip
    Zip file size: 5438595 bytes, number of entries: 1416
    ..snip..
```
    
3. Lambda 関数作成する

    ```
    $ aws lambda create-function \
    --function-name hello_nodejs \
    --zip-file fileb:///home/ec2-user/hello_nodejs.zip \
    --role arn:aws:iam::accout_id:role/lambda_basic_execution \
    --handler hello_nodejs.handler \
    --runtime nodejs8.10
```
    
4. Lambada 関数に実行ロールを設定する
    (ロールには、Lambda が SNS publish 実行できる権限を追加している前提)
    
```
    $ aws lambda add-permission \
    --function-name "hello_nodejs" \
    --statement-id "hello_nodejs" \
    --principal "logs.ap-northeast-1.amazonaws.com" \
    --action "lambda:InvokeFunction" \
    --source-arn "arn:aws:logs:ap-northeast-1:accout_id:log-group:TestLambda_NodeJS:*" \
    --source-account "accout_id"
```

5.サブスクリプションフィルターを作成する

```
    $ aws logs put-subscription-filter \
    --log-group-name TestLambda_NodeJS \
    --filter-name demo \
    --filter-pattern "" \
    --destination-arn arn:aws:lambda:ap-northeast-1:accout_id:function:hello_nodejs
    
    $ aws logs describe-subscription-filters —log-group-name TestLambda_NodeJS
```
    
6.ログを送信してテストしてみる。

``` 
    $ aws logs put-log-events —log-group-name TestLambda_NodeJS —log-stream-name stream1 —log-events "[{\"timestamp\":xxxxxxxxxxxxx , \"message\": \"Simple Lambda Test-1\"},
    {\"timestamp\":xxxxxxxxxxxxx , \"message\": \"Simple Lambda Test-2\"}]"
```
        



# hello_nodejs.js ソースコード

```hello_nodejs.js
var aws = require('aws-sdk');

var sns = new aws.SNS({
   apiVersion: '2010-03-31',
   region: 'ap-northeast-1'
});

function timer(ms, name) {
  console.log(`name: ${name} start!`)
  return new Promise((resolve, reject) => {
    setTimeout(() => resolve(name), ms)
  })
}

exports.handler = async function(event, context) {
 	console.log('publish start')
	await timer(1000, publish(event, context));

	function publish(event, context) {
		sns.publish({
	    	Message: 'Message',
	    	Subject: 'Message title ' ,
	    	TargetArn: 'arn:aws:sns:region:accountId:topicName'
	    	}, function(err, data) {
	    		if (err) {
	    		  	console.log(err.stack);
	      			return "failed publish".err.stack;
	    		}

	    		console.log('publish sent');
	    		console.log(data);
	    });
	}

};
```