LoginSignup
2
1

More than 5 years have passed since last update.

cloudwatchのalarmをslackに飛ばす(BluePrint)

Last updated at Posted at 2018-05-28

Cloudwatchのalarmをメールで確認したくない!
slackで確認したい!

やること
SlackのWebhookの準備
KMSの準備(暗号化キーの作成)
Lambda Functionの作成
IAM RoleのPolicy追加
Webhook URLの暗号化
Webhook URLの復号化設定(IAM Role)
SNSの設定
CloudWatchアラームの作成
動作確認

  • こんな感じのがやりたい blueprint-cloudwatch-slack-diagram.png

SlackWebHookUrl取得

まず、Slackのアカウントがなければ作成して下さい。
作成できたらhttps://[team名].slack.com/services/newにアクセスします。
ページ上部にある検索窓に「webhook」と入力して着信Webフックを選択
設定を追加

page.png

通知をPOSTしたいChannelを指定します。
既存のものから選択するか新規に作成して下さい。
今回は「cloudwatch」というChannelにしました。

page2.png

最後のページで「Webhook URL」が表示されるので、
コピーして控えておきましょう。Lambda Functionの作成時に必要になります。

page (1).png

後は好みでアイコンなどを設定して「Save Settings」をクリックして保存します。

KMSの準備(暗号化キーの作成)

次にWebhook URLを暗号化するキーを作成します。
環境に合わせて対象のリージョンを選択して、キーを作成して下さい。
後は全てデフォルトで作成しました。
page (2).png

page (3).png

  • 暗号化、復号化をlambdaがする必要がある為ロールを追加する page (4).png

Lambda Functionの作成

ようやくLambda Functionの作成です。
Blueprintの選択時に「slack」で検索すると
「Cloudwatch-alarm-to-slack」が3種類出てきます。
利用言語が異なるだけなので使いやすいものを選択して下さい。
page (5).png

page (6).png

page (7).png

index.js
'use strict';
/**
 * Follow these steps to configure the webhook in Slack:
 *
 *   1. Navigate to https://<your-team-domain>.slack.com/services/new
 *
 *   2. Search for and select "Incoming WebHooks".
 *
 *   3. Choose the default channel where messages will be sent and click "Add Incoming WebHooks Integration".
 *
 *   4. Copy the webhook URL from the setup instructions and use it in the next section.
 *
 *
 * To encrypt your secrets use the following steps:
 *
 *  1. Create or use an existing KMS Key - http://docs.aws.amazon.com/kms/latest/developerguide/create-keys.html
 *
 *  2. Click the "Enable Encryption Helpers" checkbox
 *
 *  3. Paste <SLACK_HOOK_URL> into the kmsEncryptedHookUrl environment variable and click encrypt
 *
 *  Note: You must exclude the protocol from the URL (e.g. "hooks.slack.com/services/abc123").
 *
 *  4. Give your function's role permission for the kms:Decrypt action.
 *      Example:
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "Stmt1443036478000",
            "Effect": "Allow",
            "Action": [
                "kms:Decrypt"
            ],
            "Resource": [
                "<your KMS key ARN>"
            ]
        }
    ]
}

 */

const AWS = require('aws-sdk');
const url = require('url');
const https = require('https');

// The base-64 encoded, encrypted key (CiphertextBlob) stored in the kmsEncryptedHookUrl environment variable
const kmsEncryptedHookUrl = process.env.kmsEncryptedHookUrl;
// The Slack channel to send a message to stored in the slackChannel environment variable
const slackChannel = process.env.slackChannel;
let hookUrl;


function postMessage(message, callback) {
    const body = JSON.stringify(message);
    const options = url.parse(hookUrl);
    options.method = 'POST';
    options.headers = {
        'Content-Type': 'application/json',
        'Content-Length': Buffer.byteLength(body),
    };

    const postReq = https.request(options, (res) => {
        const chunks = [];
        res.setEncoding('utf8');
        res.on('data', (chunk) => chunks.push(chunk));
        res.on('end', () => {
            if (callback) {
                callback({
                    body: chunks.join(''),
                    statusCode: res.statusCode,
                    statusMessage: res.statusMessage,
                });
            }
        });
        return res;
    });

    postReq.write(body);
    postReq.end();
}

function processEvent(event, callback) {
    const message = JSON.parse(event.Records[0].Sns.Message);

    const alarmName = message.AlarmName;
    //var oldState = message.OldStateValue;
    const newState = message.NewStateValue;
    const reason = message.NewStateReason;

    const slackMessage = {
        channel: slackChannel,
        text: `${alarmName} state is now ${newState}: ${reason}`,
    };

    postMessage(slackMessage, (response) => {
        if (response.statusCode < 400) {
            console.info('Message posted successfully');
            callback(null);
        } else if (response.statusCode < 500) {
            console.error(`Error posting message to Slack API: ${response.statusCode} - ${response.statusMessage}`);
            callback(null);  // Don't retry because the error is due to a problem with the request
        } else {
            // Let Lambda retry
            callback(`Server error when processing message: ${response.statusCode} - ${response.statusMessage}`);
        }
    });
}
exports.handler = (event, context, callback) => {
    if (hookUrl) {
        // Container reuse, simply process the event with the key in memory
        processEvent(event, callback);
    } else if (kmsEncryptedHookUrl && kmsEncryptedHookUrl !== '<kmsEncryptedHookUrl>') {
        const encryptedBuf = new Buffer(kmsEncryptedHookUrl, 'base64');
        const cipherText = { CiphertextBlob: encryptedBuf };

        const kms = new AWS.KMS();
        kms.decrypt(cipherText, (err, data) => {
            if (err) {
                console.log('Decrypt error:', err);
                return callback(err);
            }
            hookUrl = `https://${data.Plaintext.toString('ascii')}`;
            processEvent(event, callback);
        });
    } else {
        callback('Hook URL has not been set.');
    }
};
  • lambdaが暗号化、復号化できるようにkmsロールを追加

page (8).png

動作確認

これで準備ができたので、実際にCloudWatchでアラームを飛ばしてみます。
CloudWatchのアラーム通知はLambda関数で指定したSNSトピックに飛ぶようにします。

SNSの設定

page (9).png
page (10).png
page (11).png
page (12).png

Cloudwatchの設定

CPU使用率で監視しているので、閾値を「0以上」等にしてわざとアラーム状態にします。

page (13).png

page (14).png
page (15).png

飛んでるか確認

page (16).png

こんな感じで「cloudwatch」のChannelにアラームがpostされました。
監視間隔のタイミングで通知されるのは嬉しいですね。
あとはalarm別に表示内容変えないとログが見にくいw

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1