-
-
Save duckpuppy/23012067560414e4f407cdc4b38bbb11 to your computer and use it in GitHub Desktop.
AWS Lambda function for forwarding SNS notifications to Slack
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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(); | |
} | |
}); | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment