🕌

ECSのタスク停止イベントをSlackへ通知させる

に公開

はじめに

AWSリソースやアプリケーションの状態を、Slackに通知させたいシチュエーションは多々あるかと思います。今回は、ECSタスクが停止したタイミングでSlackチャンネルに通知する実装を、Event BridgeのAPI Destinationのみで実現してみます。※実装にはTerraformを使用します。

前提条件

  • SlackのWebhook URLは取得済み。
  • ECSクラスターは構築済み。

まとめ

Event BridgeのみでSlackに通知させること自体は可能だが、イベントのフィルタリングや通知内容のカスタマイズ性に乏しいため、間にChatBotやLambdaを噛ませた方が良さそう。

実装

resource "aws_cloudwatch_event_rule" "main" {
  name = "demo-ecs-alert"

  event_pattern = jsonencode({
    source : ["aws.ecs"],
    detail-type : ["ECS Task State Change"],
    detail : {
      clusterArn : ["arn:aws:ecs:ap-northeast-1:xxxxxxx"],
      lastStatus : ["STOPPED"]
    }
  })
}

トリガーとなる条件をevent_patternに記述します。異常終了したタスクのみ検知したいので、上記のような条件になっています。タスク状態変更イベントをログから調査するのが面倒だったので、コンソールからポチポチイベントルールを出力したが楽でした。JSON形式なのでそのままコピペできます。

resource "aws_cloudwatch_event_api_destination" "main" {
  name                             = "demo-api"
  connection_arn                   = aws_cloudwatch_event_connection.main.arn
  invocation_endpoint              = "{SlackWebhookURL}"
  invocation_rate_limit_per_second = 10
  http_method                      = "POST"
}

resource "aws_cloudwatch_event_connection" "main" {
  name               = "demo-connection"
  authorization_type = "API_KEY"

  # Slack連携では認証は不要なのでダミー
  auth_parameters {
    api_key {
      key   = "Authorization"
      value = "dummy"
    }
  }
}

ここでは、Event Bridgeの接続先を定義します。API_KEY,BASIC,OAUTH_CLIENT_CREDENTIALSいずれかの認証方式が必須のため、ダミーのAPIキーを作成します。

resource "aws_cloudwatch_event_target" "main" {
  rule     = aws_cloudwatch_event_rule.main.name
  arn      = aws_cloudwatch_event_api_destination.main.arn
  role_arn = aws_iam_role.eventbridge_role.arn

  input_transformer {
    input_paths = {
      account          = "$.account"
      region           = "$.region"
      availabilityZone = "$.detail.availabilityZone"
      stopCode         = "$.detail.stopCode"
      stoppedAt        = "$.detail.stoppedAt"
      stoppedReason    = "$.detail.stoppedReason"
      serviceName      = "$.detail.group"
    }
    input_template = <<EOF
    {
  "attachments": [
      {
          "blocks": [
              {
                  "type": "section",
                  "text": {
                      "type": "mrkdwn",
                      "text": ":warning: `<serviceName>` の ECS タスクが停止しました。 :warning:"
                  }
              },
              {
                  "type": "section",
                  "fields": [
                      {
                          "type": "mrkdwn",
                          "text": "*AWSアカウント:*\n`<account>`"
                      },
                      {
                          "type": "mrkdwn",
                          "text": "*リージョン:*\n`<region>`"
                      },
                      {
                          "type": "mrkdwn",
                          "text": "*アベイラビリティゾーン:*\n`<availabilityZone>`"
                      },
                      {
                          "type": "mrkdwn",
                          "text": "*停止コード:*\n`<stopCode>`"
                      },
                      {
                          "type": "mrkdwn",
                          "text": "*停止日時:*\n`<stoppedAt>`"
                      },
                      {
                          "type": "mrkdwn",
                          "text": "*停止理由:*\n`<stoppedReason>`"
                      }
                  ]
              }
          ]
      }
  ]
}
    EOF
  }
}

ここでSlackへの通知メッセージを書いてます。JSONパスでAWSアカウントID等の情報を定義していて、event_patternにマッチしたAWSイベントを自動的に渡しています。

# IAM Role/Policy for Event Bridge
resource "aws_iam_role" "eventbridge_role" {
  name = local.eventbridge_role_name

  assume_role_policy = jsonencode({
    Version = "2012-10-17"
    Statement = [{
      Action = "sts:AssumeRole"
      Effect = "Allow"
      Principal = {
        Service = "events.amazonaws.com"
      }
    }]
  })
}

resource "aws_iam_policy" "eventbridge_policy" {
  name = local.eventbridge_policy_name

  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [{
      Action   = "events:InvokeApiDestination"
      Effect   = "Allow"
      Resource = aws_cloudwatch_event_api_destination.main.arn
    }]
  })
}

resource "aws_iam_role_policy_attachment" "eventbridge_attachment" {
  role       = aws_iam_role.eventbridge_role.name
  policy_arn = aws_iam_policy.eventbridge_policy.arn
}

EventBridge → API Destination(外部エンドポイント呼び出し)を許可するためのロールになります。

Discussion