AppRunnerのデプロイ完了をEventBridgeの入力トランスフォーマーを使っていい感じにSlack通知した話
こんにちは!RemitAidでソフトウェアエンジニアをしているnos0611です。
はじめに
RemitAidでは一部のアプリケーションのデプロイにAWS App Runnerを使用しているのですが、デプロイが終わったかどうかはAWSコンソールで確認していました。
そこで、EventBridgeと入力トランスフォーマーを使いSlack通知する仕組みを先日構築したためご紹介します。
また、EventBridgeの入力トランスフォーマーで地味にハマったりしたのでそちらも記載します。
何が問題だったのか?
元々はこちらの画像のフローでApp Runnerへのデプロイを実施していました。

GitHub Actionsでイメージをビルドし、ECRへプッシュ、App Runnerが自動でデプロイを実行する形です。
ここで地味に問題となっていたのが、App Runnerのデプロイ完了を検知できていないことです。
GitHub Actionsの完了は検知できていましたが、ここで検知できるのはECRへのプッシュ完了までです。
デプロイが終わってから動作確認をしているため、「もう終わったかな?」とわざわざAWSコンソールを確認しに行き、「まだ終わってないからまた後で見てみるか」と出直すようなことがしばしばありました。
EventBridgeからSNS、Chatbotを経由したSlack通知を導入
こういった課題を踏まえ、以下のフローでSlack通知を導入しました。

ここでは詳細な導入手順は割愛しますが、初めて導入する場合は以下のリンクが参考になるかと思います。
ここまで用意すればひとまずSlack通知は飛んでくるのですが、以下のように割と無骨なメッセージが飛んできます。
受け取る通知がこれだけならそこまで気になりませんが、色々な通知が飛んでくるような場合ですと少し見づらいかなと思います。

入力トランスフォーマーの使用
そこで、EventBridgeの入力トランスフォーマーを使用しました。
入力トランスフォーマーを使うとイベントの情報を任意の形に変換してターゲットに送信することができます。
App Runnerからのイベントを受け取る場合は、こちらのページのようなイベント情報を受け取ることができます。
細かい制御がしたい場合やイベントにない情報が必要な場合などはLambdaなどを用意する必要がありますが、簡単な変換であれば入力トランスフォーマーで事足ります。
App Runnerイベントにはいろんな情報が含まれていますが、今回は対象のサービスが分かればいいのでserviceNameだけ使用し、以下のような通知を受け取ることができました。

最終的なTerraformのコードは以下のような形となっています。
意味合いとしては
- App Runnerに対して
OPERATION_IN_PROGRESS→RUNNINGに変更するAppRunner Service Status Changeイベントが発生した時に - イベント情報の中から
detail.serviceNameを抽出し -
input_templateの形式に変換して -
aws_sns_topic.info_notification_sns_topic.arnに送信する
といった感じです。
module "apprunner_deployed" {
source = "terraform-aws-modules/eventbridge/aws"
version = "4.1.0"
bus_name = "default"
create_bus = false
create_role = false
rules = {
apprunner_deployed = {
description = "App Runnerのデプロイ完了をSlackに通知する"
event_pattern = jsonencode({
source = ["aws.apprunner"]
detail-type = ["AppRunner Service Status Change"]
detail = {
currentServiceStatus = ["RUNNING"]
previousServiceStatus = ["OPERATION_IN_PROGRESS"]
}
})
}
}
targets = {
apprunner_deployed = [
{
name = "slack_notification"
arn = aws_sns_topic.info_notification_sns_topic.arn
attach_role_arn = module.eventbridge_role.iam_role_arn
input_transformer = {
input_paths = {
serviceName = "$.detail.serviceName"
}
// 見やすさのためにインデントを調整しています
input_template = <<EOF
{
"source": "custom",
"version": "1.0",
"content": {
"description": "${var.env}環境の<serviceName>デプロイが完了しました",
"title": ":rocket: App Runnerデプロイ完了通知"
}
}
EOF
}
}
]
}
tags = local.default_tags
}
地味にハマったポイント
input_templateに隠れ必須項目があったこと
input_templateに"source": "custom", "version": "1.0",の記述がないとうまく変換されず、通知が飛んできません。
初めはApp Runnerイベントから取得できる"source": "aws.apprunner", "version": "0"を記述していたのですがなぜかうまくいかず、インターネットを渡り歩いて設定内容に関係なくcustomと1.0が必要ということに辿り着きました。
しかもタチが悪いことに、設定はできるのに通知は飛ばない挙動になることです。
公式ドキュメントには見当たらなかったと思うのですが、もしどこかに記載があったらすみません 🙇
変数埋め込みにjsonencodeが使えないこと
これは入力トランスフォーマーというよりTerraformの話かもしれませんが、ご紹介します。
input_pathで定義したserviceNameはinput_template内で<serviceName>の形式で呼び出します。
ただ、これをTerraformのjsonencodeに渡すと変数が展開されず文字列としての<serviceName>として扱われてしまいました。
これを避けるため、最終的なコードのようにテンプレートリテラルで渡す必要があります。
まとめ
EventBridge&入力トランスフォーマーでの通知作成めっちゃ便利です!!
SNSトピック以降は使い回しが効くので、バッチ処理を増やした時などにもバッチごとの通知メッセージをサクッと用意できたりと、デプロイ通知以外でも活躍します。
告知
Podcast 「RemiTalk」では普段の会社の様子を赤裸々に語っています。
もし良ければ聴いてみてください!
Podcast 文字起こしはこちら
またRemitAid では一緒に働く仲間を募集しています。
ご興味をお持ちの方は以下のリンクからご確認ください!
Discussion