DEV Community

shimo for AWS Community Builders

Posted on

Receive Slack Notification of CodePipeline with SNS and Lambda

Motivation

I want to receive Slack notifications from CodePipeline through Amazon SNS and Lambda. By using the Lambda function, formatting and filtering of messages are available. (Therefore, not Chatbot.) In this blog, I'll share how to achieve this.

codepipeline-notify-lambda-slack

Example of Slack notification:

Slack notification

(Note: This is useful for other architectures' notifications if SNS and Lambda are used.)

Create Slack

  • Create Webhook
    • Ref: This post describe how to use webhooks in Lambda function.
  • Memo the webhook URL for Lambda environmental variables

Create Lambda

  • Create a Lambda function of Py3.9
  • Set Lambda environmental variables
    • Key: WEBHOOK_URL_SLACK, Value: https://hooks.slack.com/services/xxxxxxx
  • In this Python script, get an event from SNS.
    • If the CodePipeline status is SUCCEED, do nothing.
    • If the CodePipeline status is FAIL, send a message to Slack.
  • Permission: BasicExecutionRole will be OK.
import json
import os

import urllib3

http = urllib3.PoolManager()

WEBHOOK_URL_SLACK = os.environ["WEBHOOK_URL_SLACK"]


def gen_message_from_codepipeline_event(event_dict):
    """
    Return message according to the CodePipeline state.
    """

    # No message when SUCCEEDED
    if event_dict["detail"]["state"] == "SUCCEEDED":
        return ""

    if event_dict["detail"]["state"] == "FAILED":
        message = "CodePipeline state: FAILED"

        failed_stage = event_dict.get("additionalAttributes", {}).get("failedStage")
        stage_info = (
            f"Failed Stage: {failed_stage}"
            if failed_stage
            else "Failed Stage: N/A"
        )
        message += f"\n{stage_info}"

        failed_actions = event_dict.get("additionalAttributes", {}).get("failedActions")
        if failed_actions:
            # Send only the last try info
            info_last_action = failed_actions[-1]['additionalInformation']
            message += f"\nInformation: {info_last_action}"

        return message


def lambda_handler(event, context):
    """
    Handle CodePipeline notifications and send messages to Slack.
    """

    try:
        event_str = event["Records"][0]["Sns"]["Message"]
    except (KeyError, IndexError):
        print("Error: Event is missing required data")
        return

    event_dict = json.loads(event_str)

    # generate message
    message = gen_message_from_codepipeline_event(event_dict)
    if not message:
        print({"statusCode": 200, "body": "No message to return."})
        return
    region = event_dict["region"]
    pipeline = event_dict["detail"]["pipeline"]
    pipeline_url = f"https://{region}.console.aws.amazon.com/codesuite/codepipeline/pipelines/{pipeline}/view?region={region}"

    # Send Slack webhook
    text = f"{message}\n<{pipeline_url}|Visit CodePipeline>"
    msg = {
        "text": text,
    }
    encoded_msg = json.dumps(msg).encode("utf-8")
    resp = http.request("POST", WEBHOOK_URL_SLACK, body=encoded_msg)
    print({"statusCode": resp.status, "body": "Send message."})

    return

Enter fullscreen mode Exit fullscreen mode

Create CodePipeline

You can use any CodePipeline. If you want a simple sample, you can use a CloudFormation template I've created from the AWS document with the simple S3 and EC2 pipeline. Upload the zip file found in the section of this AWS document.
If you want to deploy to Windows Server instances using CodeDeploy, download the sample application here: SampleApp_Windows.zip.

Create notification

AWS doc is here.

On the pipeline page, select "Notify".
- Create notification rules
- Set Notification name
- Detail type: Full
- Select events: Pipeline execution Failed, Succeeded
- Targes
- Create target: SNS Topic
- Use existing SNS Topic --> Need to set access policy

My settings is as below.

Notification Setting

As a target, the SNS topic is used here. When you use an existing SNS Topic, you may need to add access policy. *The Principal is codestar-notifications.amazonaws.com. *

If you create a target topic here, the access policy is included.

target creation

  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Service": "codestar-notifications.amazonaws.com"
      },
      "Action": "SNS:Publish",
      "Resource": "arn:aws:sns:REGION:ACCOUNT_ID:TOPIC_NAME"
    },
Enter fullscreen mode Exit fullscreen mode

Subscribe

  • Subscribe the Lambda function to this SNS Topic.

Run and fail

Fail scenarios:

  • Start Pipeline without the zip file in the proper S3 bucket.
  • Start Deploy section with EC2 instances are stopped.

Failed Pipeline

Then you'll receive the Slack notification.

Slack notification

Note that the Information part is from the additionalInformation in the JSON message. And it appears on the console like the below image.
Error message

Summary

I've demonstrated how to receive Slack notifications from CodePipeline.

Top comments (2)

Collapse
 
tsevoj profile image
flawless • Edited

Great article, very helpful.
I've tried it with different pipeline from the one referenced in the article, but every time when my pipeline purposely fails on a build action I couldn't get the failed stage to be shown in the slack message, it always show Failed stage: N/A
I was debugging and saw that additionalAttributes", {} it's always empty and there is no failedActions property.

Collapse
 
shimo_s3 profile image
shimo

Thank you for your comment!
Yes, your're correct. Sometimes there is an empty additionalAttributes in the event of CodePipeline. In such a case Failed stage: N/A will be used.

I don't have much examples but this re:Post is similar case with empty attribute when using Teams. repost.aws/questions/QUIi_L8yLSQ1G...?