October 4, 2023

Sending CodeDeploy deployment notifications to Slack with Terraform

At PolymerSearch, we use CodeDeploy to deploy our services to AWS. We have a Slack channel where we post deployment notifications. We used to manually manage our infrastructure, but we recently switched to Terraform. Part of the migration required adding these Slack notifications, and since it wasn’t a straightforward process, this post will explain how to do it.

The AWS Terraform provider doesn’t provide ChatBot integration as of this writing. To achieve this, we’ve included Terraform AWS Cloud Control provider as well.

There is a manual step you have to to take to integrate AWS Chatbot with Slack. You can find the steps here. The steps are straightforward, and you’ll need to have admin access to your Slack workspace.

  1. Add AWS Chatbot to your Slack workspace

  1. Login to your AWS account, open ChatBot, select Configure New Client -> Slack.

  1. Select your Slack workspace and provide Access Permissions to AWS ChatBot.

The whole setup is available at GitHub here.

Terraform

Below are the Terraform resources we used to set up the integration. You’ll need at least two providers, hashicorp/aws and hashicorp/awscc. The first one is the official AWS provider, and the second one is the AWS Cloud Control provider. The second one is used to create the AWS Chatbot Slack channel configuration.

You’ll need to update locals with your Slack workspace ID and Slack channel ID. You can find the Slack channel ID by opening the Slack channel and copying the ID from Channel Settings -> About this channel.

Slack Workspace ID can be found in the Slack web client URL by navigating to your Slack workspace in your browser. The ID after “client/” is the Slack Workspace ID.

https://app.slack.com/client/TT896HKILP/

You can set variables to default values or pass them as command-line arguments / via your CI/CD pipeline.

ChatBot

resource "awscc_chatbot_slack_channel_configuration" "deployment_notifications" {
  configuration_name = "deployment_notifications"
  iam_role_arn       = aws_iam_role.chatbot_slack.arn
  slack_channel_id   = local.slack_deployment_notifications_channel_id
  slack_workspace_id = local.slack_workspace_id
  sns_topic_arns     = [aws_sns_topic.deployment_notifications.arn]
}

CloudWatch Log Group

resource "aws_cloudwatch_log_group" "chatbot_slack" {
  name              = "sns/${var.region}/${var.account_id}/deployment_notifications"
  retention_in_days = 7
}

CodeDeploy

resource "aws_codedeploy_app" "my_app" {
  name = "my_app"
}

resource "aws_codedeploy_deployment_group" "my_app" {
  deployment_group_name = "my_app"
  app_name              = aws_codedeploy_app.my_app.name
  service_role_arn      = aws_iam_role.codedeploy.arn
  // Rest of the configuration is omitted as that is specific to your application
}

resource "aws_iam_role" "codedeploy" {
  name               = "codedeploy"
  assume_role_policy = data.aws_iam_policy_document.assume_by_codedeploy.json
  lifecycle {
    ignore_changes = [managed_policy_arns]
  }
}

data "aws_iam_policy_document" "assume_by_codedeploy" {
  statement {
    effect  = "Allow"
    actions = ["sts:AssumeRole"]
    principals {
      type        = "Service"
      identifiers = ["codedeploy.amazonaws.com"]
    }
  }
}

CodeStar Notifications

resource "aws_codestarnotifications_notification_rule" "deployment_notifications" {
  name           = "deployment_notifications"
  detail_type    = "FULL"
  resource       = aws_codedeploy_deployment_group.my_app.arn
  event_type_ids = ["codedeploy-application-deployment-failed", "codedeploy-application-deployment-succeeded"]
  target {
    address = aws_sns_topic.deployment_notifications.arn
  }
}

IAM

data "aws_iam_policy_document" "chatbot_slack" {
  statement {
    actions = ["sts:AssumeRole"]

    principals {
      type        = "Service"
      identifiers = ["sns.amazonaws.com"]
    }
  }
}

resource "aws_iam_role" "chatbot_slack" {
  name               = "chatbot_slack"
  description        = "Allows AWS Chatbot to publish to SNS topics on your behalf."
  assume_role_policy = data.aws_iam_policy_document.chatbot_slack.json
}

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


resource "aws_iam_policy" "sns_chatbot_slack_logging" {
  name   = "sns_chatbot_slack_logging"
  policy = data.aws_iam_policy_document.sns_chatbot_slack_logging.json
}


data "aws_iam_policy_document" "sns_chatbot_slack_logging" {
  statement {
    actions = [
      "logs:CreateLogGroup",
      "logs:CreateLogStream",
      "logs:PutLogEvents",
      "cloudwatch:Describe*",
      "cloudwatch:Get*",
      "cloudwatch:List*"
    ]
    resources = [aws_cloudwatch_log_group.chatbot_slack.arn]
  }
}

Locals

locals {
  slack_workspace_id                        = "REPLACE_WITH_YOUR_SLACK_WORSKAPCE_ID"
  slack_deployment_notifications_channel_id = "REPLACE_WITH_YOUR_SLACK_CHANNEL_ID"
}

SNS

resource "aws_sns_topic" "deployment_notifications" {
  name                           = "deployment_notifications"
  http_failure_feedback_role_arn = aws_iam_role.chatbot_slack.arn
  http_success_feedback_role_arn = aws_iam_role.chatbot_slack.arn
}

resource "aws_sns_topic_subscription" "deployment_notifications" {
  topic_arn = aws_sns_topic.deployment_notifications.arn
  protocol  = "https"
  endpoint  = "https://global.sns-api.chatbot.amazonaws.com"
}

resource "aws_sns_topic_policy" "deployment_notifications" {
  arn    = aws_sns_topic.deployment_notifications.arn
  policy = data.aws_iam_policy_document.deployment_naws_sns_topic.deployment_notifications.json
}

Variables

variable "region" {
  type = string
}

variable "account_id" {
  type = number
}

2024 © Emir Ribic - Some rights reserved; please attribute properly and link back. Code snippets are MIT Licensed

Powered by Hugo & Kiss.