3
0

More than 1 year has passed since last update.

SlackでCloudWatchメトリクスを表示するAWS Chatbot ConfigurationをTerraformで自動構築する

Last updated at Posted at 2023-04-09

はじめに

CloudWatchメトリクスは色々な情報を一画面で加工して表示することができる素晴らしいサービスだが、そのために都度AWSコンソールにログインしたりコンソールをごちゃごちゃいじって表示を復元させたり、それを回避するにはJSONを打ち込みなおしたりしなければいけないという面倒くささがある。
もっと簡単にサクッとメトリクスを見ることができないか考えて、Slackで定期で確認してしまえばいいじゃない、という結論が出た。
ということで今回は、SlackでCloudWatchメトリクスのグラフ表示を定期実行するまでを自動化する。

前提知識は以下の通り。

  • CloudWatchメトリクスの基本的な知識がある
  • Terraformの基本的な知識がある

AWS Chatbotの作成に必要なTerraformプロバイダ

AWS ChatbotはGoSDKが対応していないため、Terraformの標準AWSプロバイダでは提供されていない。
ただし、AWS Cloud Control APIをラップしたAWSCCというプロバイダがあるため、今回はこれを活用する。

以下のようにawsccのprovider設定を入れておけば良い。
バージョン指定が雑なのは気にしないでいただければ。

################################################################################
# Provider                                                                     #
################################################################################
provider "aws" {
  region = "ap-northeast-1"
}

provider "awscc" {
  region = "ap-northeast-1"
}

AWS Chatbotを使う前の事前作業

ChatbotからSlackにアクセスできるよう認証をしておく必要がある。
ここだけはどうしても自動化できなかったので、AWSのマネージメントコンソールから設定しよう。

AWS Chatbotのコンソール画面で以下のボタンを押し
キャプチャ1.png

その後表示されるダイアログで、クライアントの種類で「Slack」を選択して「設定」を押下する。
image.png

成功すると、以下のように表示される。
キャプチャ3.png

AWS ChatbotのTerraform記述

以下のように設定する。

################################################################################
# Chatbot                                                                      #
################################################################################
resource "awscc_chatbot_slack_channel_configuration" "example" {
  configuration_name = "example"

  slack_workspace_id = "XXXXXXXXXXX"  // ①
  slack_channel_id   = "YYYYYYYYYYY"  // ②

  iam_role_arn = aws_iam_role.chatbot.arn
  guardrail_policies = [
    aws_iam_policy.chatbot_guardrail.arn,
  ]

  logging_level = "INFO"
}

slack_workspace_idslack_channel_idは、SlackのURLの以下の部分に該当するので、コピペしよう。

https://app.slack.com/client/XXXXXXXXXXX/YYYYYYYYYYY
                                 ①            ②

AWS側からイベントトリガで投稿する場合はSNSトピックも必要になるが、今回のSlack側からAPIを実行するユースケースでは不要だ。

IAMロールの設定

IAMは以下のように設定する。
Chatbot用のサービスポリシと、チャンネルで何ができるかというガードレールポリシというものが必要だ。
サービスポリシ ⊃ ガードレールポリシという包含関係らしく、今回チャンネル側で必要になるcloudwatch:GetMetricWidgetImageをサービスポリシから外したらエラーになったので、面倒だが両方に入れておこう。
逆に、チャンネル側ではCloudWatchは不要であるため、ガードレールポリシからは外してある。

resource "aws_iam_role" "chatbot" {
  name               = local.iam_role_name
  assume_role_policy = data.aws_iam_policy_document.chatbot_assume.json
}

data "aws_iam_policy_document" "chatbot_assume" {
  statement {
    effect = "Allow"

    actions = [
      "sts:AssumeRole",
    ]

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

resource "aws_iam_role_policy" "chatbot" {
  name   = local.iam_policy_name
  role   = aws_iam_role.chatbot.name
  policy = data.aws_iam_policy_document.chatbot_custom.json
}

data "aws_iam_policy_document" "chatbot_custom" {
  statement {
    effect = "Allow"

    actions = [
      "cloudwatch:GetMetricWidgetImage",
      "logs:PutLogEvents",
      "logs:CreateLogStream",
      "logs:DescribeLogStreams",
      "logs:CreateLogGroup",
      "logs:DescribeLogGroups",
    ]

    resources = [
      "*",
    ]
  }
}

resource "aws_iam_policy" "chatbot_guardrail" {
  name   = local.iam_chatbot_guardrail_policy_name
  policy = data.aws_iam_policy_document.chatbot_guardrail.json
}

data "aws_iam_policy_document" "chatbot_guardrail" {
  statement {
    effect = "Allow"

    actions = [
      "cloudwatch:GetMetricWidgetImage",
    ]

    resources = [
      "*",
    ]
  }
}

実行

上記でterraform apply実行後、Slack側で以下のように入力してみよう。

@aws cloudwatch get-metric-widget-image --region us-east-1 --metric-widget '{
   "view": "timeSeries",
   "stacked": false,
   "metrics": [
       [ "AWS/Billing", "EstimatedCharges", "Currency", "USD", { "period": 21600, "stat": "Maximum" } ]
   ],
   "width": 1576,
   "height": 200,
   "start": "-PT672H",
   "end": "P0D"
}'

すると、こんな感じでグラフが表示された!
image.png

ついでによく分からないサジェストが毎回表示されてうざいのでこれを消したいが、消し方がよく分からなかった……

ちなみに、以前の記事で紹介したクロスアカウントのメトリクス取得についても、設定を入れておけばcloudwatch get-metric-widget-imageで取得できるところまでは試してみた。
やっぱりダメだった。クロスアカウントのメトリクス取得をするには、対象アカウントのCloudWatch-CrossAccountSharingRoleにAssumeRoleする必要があるが、ChatbotではSTS関連の操作が封じられている上に一つの命令内でセッションを変えることもできないので、Chatbotでの実現は不可能だった。

念のため、ガードレールポリシで指定していないコマンドを実行しようとすると、しっかりエラーになってくれた。
image.png

ここで問題発生

時限実行には、Slackのリマインド機能を使えば問題ないと考えていたが、Slackのリマインド機能ではどうしても「リマインド:」という文言が入ってしまい、Chatbotがこれを解釈できずに実行エラーになってしまう。余計なリソースを作りたくないので、Slack機能で完結したいが、この「リマインド:」を消す手段、無いのだろうか……。

解決策

結局、Slackのリマインド機能は使えないので、以下のようなEventBridgeのスケジュール機能を利用する。
EventBridge Schedulerは、SlackのAPI Destinationsが使え無さそうなので、従来のEventBridgeのスケジュールルールを使う。

事前準備

API Destinationsを使用するには、Slack側の設定が必要になる。
クラスメソッド先生の記事を参考に、追加でボットを作っておいて、OAuth用のトークンを取得しておこう。
OAuth用トークンはvar.slack_api_keyで指定できるようにしておく。

IaCを書く

EventBridgeに関連する情報を以下のように定義する。

input_transformerにはターゲットに送る情報を記載する。
ここではusernameicon_emojiを変えることで、メッセージ通知元の見た目を変えることができる。
本文でメンションを送る場合は<>でユーザ名を囲めば良い。これで囲っておかないととチャットボットが反応してくれない。

事前に取得したボット用のOAuth用トークンは、aws_cloudwatch_event_connectionauth_parametersブロックで設定する。ここは認証用のヘッダ情報を設定するので、Authorizationヘッダの先頭にBearer を付与しないといけないので忘れないよう気を付けよう。

resource "aws_cloudwatch_event_rule" "example" {
  name = local.eventbridge_rule_name

  schedule_expression = "<任意の起動周期を設定>"

  is_enabled = true
}

resource "aws_cloudwatch_event_target" "example" {
  rule = aws_cloudwatch_event_rule.example.name
  arn  = aws_cloudwatch_event_api_destination.example.arn

  role_arn = aws_iam_role.eventbridge.arn

  input_transformer {
    input_template = jsonencode({
      channel : var.slack_channel_id,
      username : "料金お知らせ定期便",
      icon_emoji : ":dollar:",
      text : "<@aws> cloudwatch get-metric-widget-image --region us-east-1 --metric-widget ${templatefile("./15_metric_widget_image_template.json", {})}"
    })
  }
}

resource "aws_cloudwatch_event_api_destination" "example" {
  name                             = "slack"
  invocation_endpoint              = "https://slack.com/api/chat.postMessage"
  http_method                      = "POST"
  invocation_rate_limit_per_second = 1
  connection_arn                   = aws_cloudwatch_event_connection.example.arn
}

resource "aws_cloudwatch_event_connection" "example" {
  name               = "slack-connection"
  authorization_type = "API_KEY"

  auth_parameters {
    api_key {
      key   = "Authorization"
      value = "Bearer ${var.slack_api_key}"
    }
  }
}

これを動かすと、時限周期が来たタイミングで以下のように表示される。
キャプチャ4.png
これで、Chatbotだけに閉じて対応できないのがややイケていないが、時限で好きな情報をSlackに送れるようになった!

3
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
0