Utilizing AWS Chatbot to Monitor and Manage CodePipelines

Utilizing AWS Chatbot to Monitor and Manage CodePipelines

Get notified via Slack if builds in your pipeline have failed

AWS CodePipeline and CodeBuild are neat services for building your Continuous Integration & Delivery solution, as you don’t have to maintain servers, but can just run your build images with AWS-managed containers. As not everything is gold, one major downside is the complex & not really intuitive web interface. There’s not a single overview to highlight all jobs which are currently in a failed state over all your pipelines, so keeping an overview is difficult and tedious.

A quick and nice enhancement is the use of AWS Chatbot, which can be integrated with Slack. In this tutorial, we’ll build a solution that sends notifications about failed deployments to your slack channel of desire. Also, I’ll give some outlook on what else is possible with AWS Chatbot, like invoking Lambda functions directly.

Architecture for Chatbot and Slack Notifications that includes Lambda, SNS, and EventBridge

Integrating AWS Chatbot via SNS, Lambda & Slack

We’ll set up everything via Terraform & CloudFormation.

Setting up the CloudFormation Template for AWS Chatbot

As a starter, we need to define our CloudFormation template as YAML in a separate file aws_chatbot_template.yaml.

AWSTemplateFormatVersion: "2010-09-09"
Description: AWS Chatbot Configuration
Parameters:
  configurationName:
    Type: String
    Description: The name of the configuration.
  roleArn:
    Type: String
  logLevel:
    Type: String
  slackChannelId:
    Type: String
  slackWorkspaceId:
    Type: String
  snsTopicArn:
    Type: CommaDelimitedList

Resources:
  SlackChannelConfiguration:
    Type: AWS::Chatbot::SlackChannelConfiguration
    Properties:
      ConfigurationName: !Ref configurationName
      IamRoleArn: !Ref roleArn
      LoggingLevel: !Ref logLevel
      SlackChannelId: !Ref slackChannelId
      SlackWorkspaceId: !Ref slackWorkspaceId
      SnsTopicArns: !Ref snsTopicArn

Next, we can use our template and fill in our variables.

resource "aws_cloudformation_stack" "chatbot" {
  name          = "chatbot"
  template_body = data.local_file.template.content
  parameters = {
    configurationName = "chatbot"
    roleArn           = aws_iam_role.chatbot.arn
    logLevel          = "INFO"
    slackChannelId    = "CXXXXXXX"
    slackWorkspaceId  = "TXXXXXXX
    snsTopicArn       = aws_sns_topic.chatbot.arn
  }
}

data "local_file" "template" {
  filename = "${path.module}/aws_chatbot_template.yaml"
}

If you’re asking yourself how you find your Workspace & Channel ID, login into your Slack via the Web and click on your desired channel. Looking at the URL, you’ll find everything you need:

https://app.slack.com/client/$WORKSPACE_ID/$CLIENT_ID

Your workspace identifier starts with T and the identifier for your channel with C.

Setting up our SNS Topic & needed Roles and Policies

Next, we’re defining our role & policies we need, which is really basic for now.

resource "aws_iam_role" "chatbot" {
  name               = "chatbot"
  assume_role_policy = data.aws_iam_policy_document.assume.json
}

data "aws_iam_policy_document" "assume" {
  version = "2012-10-17"
  statement {
    actions = [
      "sts:AssumeRole"
    ]
    principals {
      identifiers = ["chatbot.amazonaws.com"]
      type        = "Service"
    }
    effect = "Allow"
  }
}
data "aws_iam_policy_document" "chatbot" {
  statement {
    effect = "Allow"
    actions = [
      "logs:*",
    ]
    resources = ["*"]
  }
}
resource "aws_iam_policy" "chatbot" {
  name        = "chatbot"
  policy      = data.aws_iam_policy_document.chatbot.json
}

resource "aws_iam_role_policy_attachment" "chatbot" {
  role       = aws_iam_role.chatbot.name
  policy_arn = aws_iam_policy.chatbot.arn
}

Next, we’re creating our SNS topic and attaching a proper policy that allows Chatbot to execute needed actions as well as EventBridge to forward our CodePipeline events.

resource "aws_sns_topic" "chatbot" {
  name   = "slack-chatbot"
  policy = data.aws_iam_policy_document.chatbot_sns.json
}
data "aws_iam_policy_document" "chatbot_sns" {
  version   = "2012-10-17"
  policy_id = "ChatBotSns"
  statement {
    sid       = "AllowSnsPublish"
    effect    = "Allow"
    resources = ["*"]
    actions   = ["sns:Publish"]
    principals {
      identifiers = ["events.amazonaws.com"]
      type        = "Service"
    }
  }

  statement {
    sid    = "AllowSnsFullAccessOnTopic"
    effect = "Allow"
    actions = [
      "sns:GetTopicAttributes",
      "sns:SetTopicAttributes",
      "sns:AddPermission",
      "sns:RemovePermission",
      "sns:DeleteTopic",
      "sns:Subscribe",
      "sns:ListSubscriptionsByTopic",
      "sns:Publish",
      "sns:Receive"
    ]
    resources = ["arn:aws:sns:*:*:slack-chatbot"]
    principals {
      identifiers = ["*"]
      type        = "AWS"
    }
  }
}

Creating Notifications at CodePipeline

We’ve already made it almost to the end. The final step is to actually forward our target events to our SNS topic, so Chatbot can send them to Slack.

data "aws_caller_identity" "current" {}
resource "aws_codestarnotifications_notification_rule" "events" {
  for_each       = toset([
    "first-pipeline",
    "second-pipeline",
    "third-pipeline"
  ])
  detail_type    = "FULL"
  event_type_ids = [
    "codepipeline-pipeline-stage-execution-failed",
    "codepipeline-pipeline-manual-approval-succeeded",
    "codepipeline-pipeline-manual-approval-needed"
  ]

  name     = "${each.value}-codepipeline"
  resource = "arn:aws:codepipeline:eu-central-1:${data.aws_caller_identity.current.account_id}:${each.value}"

  target {
    address = aws_sns_topic.chatbot.arn
  }
}

You’re not limited to the events which I used. There are many more for CodePipeline which you can find in the AWS documentation.

If everything works out properly, you should see notifications in your channel.

Sample Notification received at Slack

Taking it one step further: invoking Lambda functions

You can easily extend your Chatbot role with invocation rights for a new Lambda function. Afterward, you’ll be able to invoke your function in your Chat:

@aws lambda invoke --function-name $LAMBDA-NAME --region $REGION

A good use case for us is to submit approvals for outstanding production deployments. As you can grant your lambda function as many AWS API rights as you like, you can get as creative as possible!