Hello!

Today I want to show how with the help of AWS Lambda and Golang you can get messages in slack when Autoscaling could not create an Ec2 server. Deploy I automate using serverless.

Let’s start with the installation of node and serverless. I use nvm to manage node versions.

curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.37.2/install.sh | bash
nvm install v14.15.3
npm install -g serverless

I will create a folder and initialize the go module in it.

mkdir failed-asg
cd failed-asg
go mod init
touch main.go

In the beginning, there will be a package, imports, and main function. Main function will be responsible for executing the handler function.

package main

import (
	"bytes"
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"strings"

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

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

The main function will be handleRequest. It will generate a message based on AutoScalingEvent and send a message to slack.

func handleRequest(ctx context.Context, event events.AutoScalingEvent) (string, error) {
	url := "https://hooks.slack.com/services/token"
	var sb strings.Builder
	sb.WriteString(":siren: *EC2 Instance Launch Unsuccessful* :siren:\n")
	sb.WriteString("*Autoscaling Group Name*:")
	sb.WriteString(event.Detail["AutoScalingGroupName"].(string))
	sb.WriteString("\n")
	sb.WriteString("*StatusMessage*:")
	sb.WriteString(event.Detail["StatusMessage"].(string))
	sb.WriteString("\n")

	payload := map[string]interface{}{
		"username": "ASGFailedEvents",
		"channel":  "#test-db-alerts",
		"text":     sb.String(),
	}

	_, err := SendSlackNotification(url, payload)
	if err != nil {
		log.Fatalln("Not able to send slack message", err)
	}
	return sb.String(), nil
}

Url needs to be replaced with the value of your Slack Webhook. Using strings.Builder I create a message to be sent, by adding to it the name AutoScalingGroup and an error message. Then I form payload in which it is necessary to specify the name of the user from whom message will be sent, the channel and the text of the message and SendSlackNotification will send the message.

Let’s move on to creating the SendSlackNotification function.

func SendSlackNotification(url string, data interface{}) (bodyString string, err error) {
	jsonString, _ := json.Marshal(data)

	req, err := http.NewRequest("programming", url, bytes.NewBuffer(jsonString))
	req.Header.Set("Content-Type", "application/json")

	client := &http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		panic(err)
	}

	defer resp.Body.Close()

	body, _ := ioutil.ReadAll(resp.Body)
	bodyString = string(body)

	if resp.StatusCode != 200 {
		fmt.Println("Response Status:", resp.Status) // 200 OK
		fmt.Println("Response Headers:", resp.Header)
		fmt.Println("Response Body:", bodyString)

		return bodyString, errors.New("can't work with 42")
	}

	return bodyString, nil
}

Here json is formed from the previously created payload and with the help of http the request made to Webhook Url. Then it is checked whether the request was successfully sent.

Let’s go back to serverless for lambda deployment. At the root of the repository, I create a file serverless.yml with content

service: asg-failed-events
provider:
  name: aws
  runtime: go1.x
package:
  exclude:
    - ./**
  include:
    - ./bin/**
functions:
  lambda-time:
    handler: bin/failed-asg-event-notification
    events:
      - cloudwatchEvent:
          event:
            source:
              - "aws.autoscaling"
            detail-type:
              - "EC2 Instance Launch Unsuccessful"

This configuration describes the name of the lambda that will be created and lambda trigger, in this case, it is EC2 Instance Launch Unsuccessful event from aws.autoscaling passed to the lambda by cloudwatchEvent.

Now you need to install all the dependencies, compile the code and deploy.

go mod tidy
OOS=linux GOARCH=amd64  go build -o bin/failed-asg-event-notification .
sls deploy

After that, if there is an event of the type EC2 Instance Launch Unsuccessful from aws.autoscaling the corresponding message will be sent to the slack.