Skip to content

Instantly share code, notes, and snippets.

@zyphlar
Last active February 6, 2019 12:00
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save zyphlar/7eb181a4aa6d21b6674b6c2dc4af2104 to your computer and use it in GitHub Desktop.
Save zyphlar/7eb181a4aa6d21b6674b6c2dc4af2104 to your computer and use it in GitHub Desktop.
See comments
'use strict';
/**
* See http://notes.webutvikling.org/send-aws-cloudwatch-alarms-to-slack/ for instructions
* But I like this code better because the snsToSlack.js code doesn't parse the CloudWatch JSON
*
* 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 hookUrl environment variable, OR follow the encryption instructions below.
*
* 5. Type the channel for messages to be sent into the slackChannel environment variable.
*
*
* 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;
const hookUrl = process.env.hookUrl;
var goodStates = [
"OK"
];
var badStates = [
"ALARM"
];
// unused because default is warning
//var neutralStates = [
// "INSUFFICIENT_DATA"
//];
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 subject = event.Records[0].Sns.Subject;
const message = JSON.parse(event.Records[0].Sns.Message);
// start the process with a default severity.
var severity = "warning";
const alarmName = message.AlarmName;
//var oldState = message.OldStateValue;
const newState = message.NewStateValue;
const reason = message.NewStateReason;
for(var state in badStates) {
if (newState.indexOf(badStates[state]) != -1) {
severity = "danger";
break;
}
}
// only check for good states if state hasn't changed
if (severity == "warning") {
for(var state in goodStates) {
if (newState.indexOf(goodStates[state]) != -1) {
severity = "good";
break;
}
}
}
const slackMessage = {
channel: slackChannel,
attachments: [{
color: severity,
text: `${alarmName} state is now ${newState}: ${reason}`,
}],
text: "*" + subject + "*",
icon_emoji: ":lightning_cloud:"
};
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.');
}
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment