😎

Terraformを使ったECS停止時のカスタムSlack通知の実装

2024/10/13に公開

1.記事を書いた理由

  • 業務でECSの異常停止時にSlackへ通知する仕組みを導入していますが、デフォルトの通知内容だと見辛く、エラーコードや停止理由の確認がAWSコンソールに慣れていない人には難しいという課題がありました

2.構成

  • 何らかの理由でECSが停止した際にEventBridgeで設定したルールでキャッチし、SNS,Chatbot経由でSlackへ通知する構成です。
  • Lambdaでも実現出来るかと思いますが、今回は細かい要件を想定しておらず運用工数的にもChatbotで良い感じに通知してくれる構成にしています。
  • クラスメソッドさんの記事を大変参考にさせていただきました。
    https://dev.classmethod.jp/articles/ecs-task-stop-reason-slack-notification/

デフォルトの通知内容

  • ECSが停止したことは分かるのですが、この通知内容だとエラーコトードや停止理由が一目で分からず、すぐに現状把握することが出来ません。

今回設定するカスタマイズの通知イメージ

  • 停止コードや理由の記載により、デフォルトの通知内容と比較して現状把握しやすくなったと思います。
  • 細かい通知項目やUIはお好みでカスタマイズして下さい。

3.各サービスのコード

前提

  • 本記事では通知内容をカスタマイズしている点に焦点を当てたいため、ECS含めたWebまでのアクセスに必要なリソース、IAM等のセキュリティ周りの記載は省略しています。

①EventBridge

resource "aws_cloudwatch_event_rule" "ecs_event" {
  state       = "ENABLED"
  name        = "ecs-event-notify"
  description = "ecs alert notification rule"
  event_bus_name = "default"

  event_pattern = <<END
    {
      "source": ["aws.ecs"],
      "detail-type": [
        "ECS Task State Change"
      ],
      "detail": {
        "lastStatus": [
          "STOPPED"
        ],
        "clusterArn": [
          "${aws_ecs_cluster.web.arn}"
        ]
      }
    }
  END
}

resource "aws_cloudwatch_event_target" "ecs_event" {
  target_id = "ecs-event-notify"
  rule      = aws_cloudwatch_event_rule.ecs_event.name
  arn       = module.sns_notify_chatbot.topic_arn

  input_transformer {
    input_paths = {
      "account": "$.account",
      "availabilityZone": "$.detail.availabilityZone",
      "clusterArn": "$.detail.clusterArn",
      "group": "$.detail.group",
      "resource": "$.resources[0]",
      "stoppedAt": "$.detail.stoppedAt",
      "stopCode": "$.detail.stopCode",
      "stoppedReason": "$.detail.stoppedReason"
    }

    input_template = <<END
    {
      "version": "1.0",
      "source": "custom",
      "content": {
        "textType": "client-markdown",
        "title": ":warning: ECS のタスクが停止されました :warning:",
        "description": "overview\n・ACCOUNT_ID: `<account>`\n・AZ: `<availabilityZone>`\n ・Service:`<group>`\n・Task: `<resource>`\n・stoppedAt: `<stoppedAt>`\n・stopCode: `<stopCode>`\n・stoppedReason: `<stoppedReason>`"
      }
    }
    END
  }
}

②SNS

  • EventBridgeのサービスエンドポイントがSNSをPublish出来るよう設定しています。
// ref: https://github.com/terraform-aws-modules/terraform-aws-sns
module "sns_notify_chatbot" {
  source  = "terraform-aws-modules/sns/aws"
  version = "6.1.1"

  create = true
  name = "slack_notify"
  display_name = "slack_notify"

  create_topic_policy = false
  topic_policy = data.aws_iam_policy_document.sns_notify_chatbot.json
}

data "aws_caller_identity" "current" {}

data "aws_iam_policy_document" "sns_notify_chatbot" {
  statement {
    sid = "AWSEvents_EcsEvent"
    effect = "Allow"
    principals {
      type        = "Service"
      identifiers = ["events.amazonaws.com"]
    }
    actions = ["sns:Publish"]
    resources = [
      "arn:aws:sns:ap-northeast-1:${data.aws_caller_identity.current.account_id}:slack_notify"
    ]
  }
}

③Chatbot

  • 個人環境で複数のChatbotを作成してしまっているためfor_eachを用いていますが、今回のケースは特に必要無いです。
locals {
  chatbots = {
    personal = {
      name               = {お好きな名前を付与}
      slack_workspace_id = {SlackのワークスペースID}
      slack_channel_id   = {SlackのチャンネルID}
    }
  }
}

resource "awscc_chatbot_slack_channel_configuration" "example" {
  for_each           = { for k, v in local.chatbots : k => v }
  configuration_name = each.key
  iam_role_arn       = aws_iam_role.chatbot.arn // 今回IAMロールの設定の記載は焼灼しています。
  guardrail_policies = [
    "arn:aws:iam::aws:policy/AdministratorAccess"
  ]
  slack_channel_id   = each.value.slack_channel_id
  slack_workspace_id = each.value.slack_workspace_id
  logging_level      = "ERROR"
  sns_topic_arns     = [
    module.sns_notify_chatbot.topic_arn // 通知を受け取るSNSを指定。
  ]
  user_role_required = true

  tags = [{
    key   = "Name"
    value = each.key
  }]
}

4.最後に

  • 通知内容をよりカスタマイズしたい場合はLambdaでも良いかと思いますが、ランタイムのバージョンアップ等の管理面が面倒なため、細かい要件がなければ今回のようにシンプルな構成でも良いと思います。
  • 最後まで読んでくださりありがとうございました。
GitHubで編集を提案

Discussion