Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Slack Notification with CloudWatch Alarms & Lambda

DZone's Guide to

Slack Notification with CloudWatch Alarms & Lambda

While your teammates are chatting, wouldn't it be helpful to get some alerts using Slack as well? Learn how to integrate it here.

· Cloud Zone ·
Free Resource

Easily enforce open source policies in real time and reduce MTTRs from six weeks to six seconds with the Sonatype Nexus Platform. See for yourself - Free Vulnerability Scanner. 

ChatOps has emerged as one of the most effective techniques to implement DevOps. Hence, it will be great to receive notifications and infrastructure alerts into collaboration messaging platforms like Slack & HipChat.

AWS CloudWatch Alarms and SNS are a great mix to build a real-time notification system as SNS supports multiple endpoints (Email, HTTP, Lambda, SQS). Unfortunately, SNS doesn't support out of the box sending notifications to tools like Slack.

CloudWatch will trigger an alarm to send a message to an SNS topic if the monitoring data gets out of range. A Lambda function will be invoked in response to SNS receiving the message and will call the Slack API to post a message to Slack channel.

To get started, create an EC2 instance using the AWS Management Console or the AWS CLI:

Next, create a SNS topic:

aws ec2 run-instances --image-id ami_ID
    --count 1 --instance-type t2.micro
    --key-name KEY_NAME --security-group-ids SG_ID
    --region us-east-1 
    --tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=demo}]'


Then, setup a CloudWatch alarm when the instance CPU utilization is higher than 40% and send a notification to the SNS topic:

aws sns create-topic --name cpu-utilization-alarm-topic --region us-east-1


As a result:

To be able to post messages to a Slack channel, we need to create a Slack Incoming WebHook. Start by setting up an incoming webhook integration in your Slack workspace:

Note down the returned WebHook URL for upcoming part.

The Lambda handler function is written in Go, it takes as an argument the SNS message. Then, it parses it and queries the Slack API to post a message to the Slack channel configured in the previous section:

package main

import (
"bytes"
"encoding/json"
"fmt"
"log"
"net/http"
"os"

"github.com/aws/aws-lambda-go/lambda"
)

type Request struct {
Records []struct {
SNS struct {
Type       string `json:"Type"`
Timestamp  string `json:"Timestamp"`
SNSMessage string `json:"Message"`
} `json:"Sns"`
} `json:"Records"`
}

type SNSMessage struct {
AlarmName      string `json:"AlarmName"`
NewStateValue  string `json:"NewStateValue"`
NewStateReason string `json:"NewStateReason"`
}

type SlackMessage struct {
Text        string       `json:"text"`
Attachments []Attachment `json:"attachments"`
}

type Attachment struct {
Text  string `json:"text"`
Color string `json:"color"`
Title string `json:"title"`
}

func handler(request Request) error {
var snsMessage SNSMessage
err := json.Unmarshal([]byte(request.Records[0].SNS.SNSMessage), &snsMessage)
if err != nil {
return err
}

log.Printf("New alarm: %s - Reason: %s", snsMessage.AlarmName, snsMessage.NewStateReason)
slackMessage := buildSlackMessage(snsMessage)
postToSlack(slackMessage)
log.Println("Notification has been sent")
return nil
}

func buildSlackMessage(message SNSMessage) SlackMessage {
return SlackMessage{
Text: fmt.Sprintf("`%s`", message.AlarmName),
Attachments: []Attachment{
Attachment{
Text:  message.NewStateReason,
Color: "danger",
Title: "Reason",
},
},
}
}

func postToSlack(message SlackMessage) error {
client := &http.Client{}
data, err := json.Marshal(message)
if err != nil {
return err
}

req, err := http.NewRequest("POST", os.Getenv("SLACK_WEBHOOK"), bytes.NewBuffer(data))
if err != nil {
return err
}

resp, err := client.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()

if resp.StatusCode != 200 {
fmt.Println(resp.StatusCode)
return err
}

return nil
}

func main() {
lambda.Start(handler)
}


As Go is a compiled language, build the application and create a Lambda deployment package using the bash script below:

#!/bin/bash

echo "Building binary"
GOOS=linux GOARCH=amd64 go build -o main main.go

echo "Create deployment package"
zip deployment.zip main

echo "Cleanup"
rm main


Once created, use the AWS CLI to deploy the function to Lambda. Make sure to override the Slack WebHook with your own:

aws lambda create-function --function-name SlackNotification
    --zip-file fileb://./deployment.zip
    --runtime go1.x --handler main
    --role IAM_ROLE_ARN
    --environment Variables={SLACK_WEBHOOK=https://hooks.slack.com/services/TOKEN}
    --region us-east-1


Note: For non Gophers you can download the zip file directly from here.

From here, configure the invoking service for your function to be the SNS topic we created earlier:

aws sns subscribe --topic-arn TOPIC_ARN
    --protocol lambda
    --notification-endpoint LAMBDA_ARN
    --region us-east-1


Lambda Dashboard:

Let's test it out, connect to your instance via SSH, then install stress which is a tool to do workload testing:

sudo yum install -y stress


Issue the following command to generate some workloads on the CPU:

stress --cpu 2 --timeout 60s


You should receive a Slack notification as below:

Note: You can go further and customize your Slack message.

Automate open source governance at scale across the entire software supply chain with the Nexus Platform. Learn more.

Topics:
cloud ,chatops ,slack ,chat integration ,cloud monitoring ,cloud alerting

Published at DZone with permission of

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}