SNS + Lambda + Twilio で音声電話をかける v2 を社内で披露していたら、「Slack にも通知が欲しいよね」とステキな提案をしてくれたメンバーがいたので、ついでに実装しました。
表示される内容は以下の様な感じになります。
7/1 追記:
CloudWatch から上がる Alarm については、State の状態で色を変えつつ JSON メッセージを解析してわかり易く表示、それ以外の通知に関してはそのまま表示しています。
コード
package.json
Slack には API が用意されているため、自前で call してもいいのですが、npm パッケージでさくっと解決することに。
始めは Slack 純正の node-slack-client の利用を考えたのですが、一部 native build が必要なパッケージを利用していて Lambda での利用がちょっと面倒なため、Webhook と API の両方に対応している slack-node を選択。
{
"name": "lambda-slack-alert",
"version": "1.0.0",
"description": "",
"author": "",
"license": "ISC",
"dependencies": {
"moment": "^2.10.3",
"slack-node": "^0.1.3"
},
"devDependencies": {
"coffee-script": "^1.9.3",
"del": "^1.2.0",
"gulp": "^3.9.0",
"gulp-coffee": "^2.3.1",
"gulp-zip": "^3.0.2",
"run-sequence": "^1.1.1"
}
}
gulpfile
gulpfile は Twilio 版ほぼそのまま。
パッケージ化対象の node_modules の部分だけ修正。
gulp = require 'gulp'
coffee = require 'gulp-coffee'
zip = require 'gulp-zip'
del = require 'del'
runSequence = require 'run-sequence'
gulp.task 'clean', (cb) ->
del 'build', cb
gulp.task 'coffee', ->
gulp.src 'src/*.coffee'
.pipe coffee bare: true
.pipe gulp.dest 'build'
gulp.task 'copy', ->
gulp.src 'node_modules/@(moment|slack-node)/**'
.pipe gulp.dest 'build/node_modules'
gulp.task 'zip', ['coffee', 'copy'], ->
gulp.src 'build/**'
.pipe zip 'lambda.zip'
.pipe gulp.dest 'dist'
gulp.task 'build', ->
runSequence 'clean', 'zip'
lambda 用コード本体
apiToken
と channel
には、Slack 側の情報を入れます。
自分の場合は、Slack を無料利用していて Integration の数を増やしたくなかったので、Hubot 用の API Token を使い回しています。
メッセージの見栄えを良くするため、Attachments を使っています。好みに応じていろいろカスタマイズしてみてもいいでしょう。
apiToken = 'YOUR_SLACK_API_TOKEN'
channel = 'YOUR_SLACK_CHANNEL'
moment = require 'moment'
Slack = require 'slack-node'
slack = new Slack apiToken
exports.handler = (event, context) ->
try
color = 'danger'
title = 'アラートが発生しました。速やかにシステムの状態を確認し、対応して下さい。'
if event.Records[0].Sns.Subject.match /^OK: /
color = 'good'
title = 'システムの状態が正常になりました。'
try
# try to detect CloudWatch alarm
data = JSON.parse event.Records[0].Sns.Message
payload =
as_user: true
channel: channel
attachments: JSON.stringify [
fallback: title
text: "*#{event.Records[0].Sns.Subject}*"
pretext: title
color: color
mrkdwn_in: ['pretext', 'text', 'fields']
fields: [
title: 'Description'
value: "#{data.AlarmDescription}"
short: false
,
title: 'Reason'
value: data.NewStateReason
,
short: false
title: 'Datetime'
value: moment(data.StateChangeTime).utcOffset(9).format('YYYY-MM-DD HH:mm:ss')
short: false
]
]
catch e
# other messages
payload =
as_user: true
channel: channel
attachments: JSON.stringify [
fallback: title
text: "*#{event.Records[0].Sns.Subject}*"
pretext: title
color: color
mrkdwn_in: ['pretext', 'text', 'fields']
fields: [
value: event.Records[0].Sns.Message
short: false
]
]
catch e
context.done null, 'Invalid event data.'
return
console.log 'Start message post'
slack.api 'chat.postMessage', payload, (err, response) ->
if err
console.error err
context.done null, 'Post failed.'
return
console.log response
context.done null, 'Post succeeded.'
return
ビルド、function 更新
ビルドおよび Lambda の更新は、SNS + Lambda + Twilio で音声電話をかける v2 と同じため、省略。
テスト実行
event データを付けずに単に invoke
しても何も起こりません。
console で event データを用意してもいいのですが面倒なので、AWS Management Console から Lambda の Edit/Test に行き、Sample event で SNS を選択し、適当にメッセージをカスタマイズしてテストしておきます。
Amazon SNS との連携
作成された function の ARN をコピーし、SNS の「Create Subscription」で AWS Lambda を選択、コピーした ARN を貼り付けて登録します。
詳細な手順は、ここ を参照してください。
終わりに
Amazon SNS に通知系を集約すれば、AWS Lambda を用いて様々な Output が可能になるので、非常に便利です。
メール通知だけでなく、Twilio での電話通知や Slack への Post 等、少人数で効率的に回す仕掛けが安価且つ簡単に作れるのは非常にありがたいですね。
AWS の利便性にどっぷり浸かってしまい、そう簡単には抜けられそうにありません。。
- 7/1
- CloudWatch の通知の場合にメッセージ内容と色を変更するよう修正しました