Skip to content

Instantly share code, notes, and snippets.

@benyanke
Forked from vgeshel/function.js
Last active July 7, 2021 16:02
Show Gist options
  • Save benyanke/862e446e5a816551928d8acc2d98b752 to your computer and use it in GitHub Desktop.
Save benyanke/862e446e5a816551928d8acc2d98b752 to your computer and use it in GitHub Desktop.
AWS Lambda function for forwarding SNS notifications to Slack
// Added by Ben Yanke
// from https://gist.github.com/benyanke/862e446e5a816551928d8acc2d98b752
console.log('Loading function');
const https = require('https');
const url = require('url');
// SETUP
// urlToUse = in this environment variable, place the name of another environment variable which contains the key.
// This allows easy dev/prod switching.
// For example:
// urlToUse = 'devkey'
// devkey = 123456
// prodkey = 654321
// Easily switch between dev and prod by changing urlToUse.
// to get the slack hook url, go into slack admin and create a new "Incoming Webhook" integration
const slack_url = "https://hooks.slack.com/services/" + process.env[process.env.urlToUse]
const slack_req_opts = url.parse(slack_url);
slack_req_opts.method = 'POST';
slack_req_opts.headers = {
'Content-Type': 'application/json'
};
exports.handler = function(event, context) {
(event.Records || []).forEach(function(rec) {
if (rec.Sns) {
var req = https.request(slack_req_opts, function(res) {
if (res.statusCode === 200) {
context.succeed('posted to slack');
} else {
context.fail('status code: ' + res.statusCode);
}
});
req.on('error', function(e) {
console.log('problem with request: ' + e.message);
context.fail(e.message);
});
const has = Object.prototype.hasOwnProperty; // cache the lookup once, in module scope.
try {
// var msg = JSON.parse(rec.Sns.Message);
var msg = rec.Sns.Message;
console.log(msg);
} catch (err) {
console.log(err)
}
// If event is a CodeDeploy event
if (has.call(msg, "deploymentId")) {
deploymentOverview = JSON.parse(msg.deploymentOverview);
// Check state
if (deploymentOverview.Message.Failed > 0 || msg.status == 'FAILED') {
color = "danger";
} else {
color = "good";
}
// Output error msg if it exists
if (has.call(msg, "errorInformation")) {
errorInformation = JSON.parse(msg.errorInformation);
textMsg = errorInformation.ErrorMessage;
} else {
textMsg = "Message not set";
}
// Send to slack
req.write(JSON.stringify({
"attachments": [{
"fallback": "CodeDeploy: " + cloudWatchMessage.AlarmName + " was triggered in " + cloudWatchMessage.Region,
"pretext": "CodeDeploy",
"title": "CodeDeploy: Status Updated to '" + msg.status + "' for deployment " + msg.deploymentId,
"title_link": 'TITLE LINK',
"text": textMsg,
"color": color,
"author_name": "Application: " + msg.applicationName,
"author_link": 'LINK',
"fields": [{
"title": "Instances Succeeded",
"value": deploymentOverview.Succeeded,
"short": true
},
{
"title": "Instances Failed",
"value": deploymentOverview.Failed,
"short": true
},
{
"title": "Instances Skipped",
"value": deploymentOverview.Skipped,
"short": true
},
{
"title": "Instances In Progress",
"value": deploymentOverview.InProgress,
"short": true
},
{
"title": "Instances Pending",
"value": deploymentOverview.Pending,
"short": true
}
]
}]
}))
}
// If event is a CloudWatch event
else if (rec.Sns.Subject.startsWith("ALARM:") || rec.Sns.Subject.startsWith("OK:")) {
cloudWatchMessage = JSON.parse(rec.Sns.Message)
linkToInstance = "https://" + cloudWatchMessage.Region.toLowerCase() +
".console.aws.amazon.com/ec2/v2/home?region=" +
cloudWatchMessage.Region.toLowerCase() + "#Instances:" +
cloudWatchMessage.Trigger.Dimensions[0].name +
"=" + cloudWatchMessage.Trigger.Dimensions[0].value +
";sort=tag:Name";
linkToAllInstances = "https://" + cloudWatchMessage.Region.toLowerCase() +
".console.aws.amazon.com/ec2/v2/home?region=" +
cloudWatchMessage.Region.toLowerCase() + "#Instances:sort=tag:Name";
linkToVpnConnections = "https://" + cloudWatchMessage.Region.toLowerCase() +
".console.aws.amazon.com/vpc/home?region=" +
cloudWatchMessage.Region.toLowerCase() + "#VpnConnections";
linkToConsole = "https://" + cloudWatchMessage.Region.toLowerCase() +
".console.aws.amazon.com/console/home?region=" +
cloudWatchMessage.Region.toLowerCase();
// Handle different types of errors
// EC2
if (cloudWatchMessage.Trigger.Namespace == "AWS/EC2") {
consoleLink = "<" + linkToInstance + "|Click here to open affected EC2 instance>";
// VPN
} else if (cloudWatchMessage.Trigger.Namespace == "AWS/VPN") {
consoleLink = "<" + linkToVpnConnections + "|Click here to open VPN connections>";
// All others
} else {
consoleLink = "<" + linkToConsole + "|Click here to open AWS console>";
}
if (rec.Sns.Subject.startsWith("OK:")) {
var color = "good";
var type = "Alarm resolved";
} else {
var color = "danger"
var type = "ALARM TRIGGERED";
}
req.write(JSON.stringify({
"attachments": [{
"fallback": "CloudWatch " + type + ": " + cloudWatchMessage.AlarmName + " in " + cloudWatchMessage.Region,
"pretext": "New CloudWatch Alert",
"title": "CloudWatch Alarm: " + cloudWatchMessage.AlarmName,
"title_link": linkToAllInstances,
"text": cloudWatchMessage.NewStateReason,
"color": color,
"author_name": "Service: " + cloudWatchMessage.Trigger.Namespace,
"author_link": linkToAllInstances,
"fields": [{
"title": "Alarm",
"value": cloudWatchMessage.AlarmName,
"short": true
},
{
"title": "Alarm Description",
"value": cloudWatchMessage.AlarmDescription,
"short": true
},
{
"title": "Alarm Status",
"value": type,
"short": true
},
{
"title": "Region",
"value": cloudWatchMessage.Region,
"short": true
},
{
"title": "Environment",
"value": cloudWatchMessage.Trigger.Namespace,
"short": true
},
{
"title": "AWS Console",
"value": consoleLink,
"short": true
}
]
}]
}))
}
// If event is an autoscale Event
else if (rec.Sns.Subject.startsWith("Auto Scaling: launch")) {
cloudWatchMessage = JSON.parse(rec.Sns.Message)
linkToInstance = "https://" + cloudWatchMessage.Region.toLowerCase() +
".console.aws.amazon.com/ec2/v2/home?region=" +
cloudWatchMessage.Region.toLowerCase() + "#Instances:" +
cloudWatchMessage.Trigger.Dimensions[0].name +
"=" + cloudWatchMessage.Trigger.Dimensions[0].value +
";sort=tag:Name";
linkToAllInstances = "https://" + cloudWatchMessage.Region.toLowerCase() +
".console.aws.amazon.com/ec2/v2/home?region=" +
cloudWatchMessage.Region.toLowerCase() + "#Instances:sort=tag:Name";
linkToVpnConnections = "https://" + cloudWatchMessage.Region.toLowerCase() +
".console.aws.amazon.com/vpc/home?region=" +
cloudWatchMessage.Region.toLowerCase() + "#VpnConnections";
linkToConsole = "https://" + cloudWatchMessage.Region.toLowerCase() +
".console.aws.amazon.com/console/home?region=" +
cloudWatchMessage.Region.toLowerCase();
// Handle different types of errors
// EC2
if (cloudWatchMessage.Trigger.Namespace == "AWS/EC2") {
consoleLink = "<" + linkToInstance + "|Click here to open affected EC2 instance>";
// VPN
} else if (cloudWatchMessage.Trigger.Namespace == "AWS/VPN") {
consoleLink = "<" + linkToVpnConnections + "|Click here to open VPN connections>";
// All others
} else {
consoleLink = "<" + linkToConsole + "|Click here to open AWS console>";
}
req.write(JSON.stringify({
"attachments": [{
"fallback": "Scaling Alert: " + cloudWatchMessage.AlarmName + " was triggered in " + cloudWatchMessage.Details,
"pretext": "New AutoScaling Alert",
"title": "AutoScaling Up: " + cloudWatchMessage.Service,
"title_link": linkToAllInstances,
"text": cloudWatchMessage.NewStateReason,
"color": "danger",
"author_name": "Service: " + cloudWatchMessage.Trigger.Namespace,
"author_link": linkToAllInstances,
"fields": [{
"title": "Alarm",
"value": cloudWatchMessage.AlarmName,
"short": true
},
{
"title": "Alarm Description",
"value": cloudWatchMessage.AlarmDescription,
"short": true
},
{
"title": "Region",
"value": cloudWatchMessage.Region,
"short": true
},
{
"title": "Environment",
"value": cloudWatchMessage.Trigger.Namespace,
"short": true
},
{
"title": "AWS Console",
"value": consoleLink,
"short": true
}
]
}]
}))
}
// If event is an RDS Event
else if (rec.Sns.Subject.startsWith("RDS")) {
message = JSON.parse(rec.Sns.Message)
var messagesToIgnore = ["Finished DB Instance backup", "Backing up DB instance"]
var eventMsg = message['Event Message'];
// Ignore certain types of message
if (messagesToIgnore.includes(eventMsg)) {
console.log("Event message was " + eventMsg + " - ignoring and not sending notification to slack");
return;
}
req.write(JSON.stringify({
text: "RDS Notification: \n DB '" + message['Source ID'] + "' : " + eventMsg
}))
}
// Otherwise, if not a CloudWatch event, send full alert subject and message
else {
req.write(JSON.stringify({
text: "*Subject:* " + rec.Sns.Subject + "\n *Message:* " + rec.Sns.Message
}))
}
req.end();
}
});
};
@farmerbean
Copy link

Get an error?
Cannot read property 'name' of undefined, line 41

node 6.10 runtime

@benyanke
Copy link
Author

benyanke commented Jun 5, 2018

@farmerbean I didn't see this until now - I'm not even sure what line 41 was back in dec.

Let me know if it still doesn't work and I'll give you a hand - we're using it daily here still!

@harryhan24
Copy link

this is really awesome 👍

@alarickus
Copy link

Hello.. could you help me fix on this part?

            linkToConsole = "https://" + cloudWatchMessage.Region.toLowerCase() +
                ".console.aws.amazon.com/console/home?region=" +
                cloudWatchMessage.Region.toLowerCase();

my console link is broken and routed me to this.
https://us/%20west%20-%20oregon.console.aws.amazon.com/console/home?region=us%20west%20-%20oregon

i want to fix it like this
https://console.aws.amazon.com/cloudwatch/home?region=ap-northeast-2#s=Alarms&"alarmname"

TIA

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment