最近CodeBuildを利用し始めたのですが、CircleCI等のようにはサクッとSlack通知ができないのを少しもどかしく感じました。ビルドの状態がSlack上で楽に把握できないと、コンソールを見に行ってしまったり思考リソースを奪われてしまいますよね。
そこでよし通知をしようと思っても、まさにこれというTerraform Moduleがなかったのでmoduleを作ってみました。
en30/codebuild-to-slack/aws | Terraform Module Registry
これを利用するとCodeBuild上での以下のイベントを簡単にSlack通知できます。
-
IN_PROGRESS
: 開始 -
SUCCEEDED
: 成功 -
FAILED
: 失敗 -
FAULT
: AWS側が原因での失敗? -
TIMED_OUT
: タイムアウト -
STOPPED
: 中止
使い方
variable "encrypted_slack_webhook_url" {}
resource "aws_kms_key" "slack_webhook_url" {
description = "Key for Slack Webhook URL"
}
module "codebuild_notification" {
source = "en30/codebuild-to-slack/aws"
version = "0.0.1"
encrypted_slack_webhook_url = "${var.encrypted_slack_webhook_url}"
slack_channel = "#app"
kms_key_arn = "${aws_kms_key.slack_webhook_url.arn}"
}
encrypted_slack_webhook_url
は以下のようにAWS CLIを使うことで得ることができます。
$ aws kms encrypt --key-id $AWS_KMS_KEY_ID --plaintext $SLACK_WEBHOOK_URL --query CiphertextBlob --output text
$AWS_KMS_KEY_ID
はmoduleへkms_key_arn
として渡しているkeyのid、$SLACK_WEBHOOK_URL
はSlackのIncoming WebhookのURLです。
上の例のようにキーもTerraformで作ろうとすると
-
aws_kms_key
の作成(terraofmr apply
①) - キーを利用してwebhook urlを暗号化
- moduleを利用した通知設置(
terraofrm apply
②)
とterraform apply
で一発でいけないのが気持ち悪いところですが、よろしければ使ってみてください!
中身の簡単な説明
やっていることとしては
- CodeBuildのビルド状態が変化に関するCloudWatch EventでLambdaを起動
- Lambdaでイベント情報を整形してSlack通知
です。LambdaにRuby Runtimeも入ったことですし、僕はRubyが好きなのでLambdaはRubyで書きました。
それほど面白いところがあるわけではないですが、Lambdaのデプロイをシンプルに済ますために、標準ライブラリ、Lambdaの環境に元々入っているaws-sdk
だけで済ますようにしています。
require "uri"
require "net/http"
require "json"
require "base64"
require "aws-sdk"
COLORS = {
"SUCCEEDED" => "good",
"FAILED" => "danger",
"FAULT" => "danger",
"TIMED_OUT" => "danger",
"STOPPED" => "warning",
}.freeze
def decrypt(encrypted_url)
client = Aws::KMS::Client.new
client.decrypt(ciphertext_blob: Base64.decode64(encrypted_url)).plaintext
end
def build_url(region, project, slug)
region, project, slug = [region, project, slug].map(&URI.method(:encode_www_form_component))
"https://#{region}.console.aws.amazon.com/codesuite/codebuild/projects/#{project}/build/#{slug}/log"
end
def format(event) # rubocop:disable Metrics/MethodLength
project = event["detail"]["project-name"]
status = event["detail"]["build-status"]
slug = event["detail"]["build-id"].split("/").last
{
attachments: [
{
color: COLORS[status],
title: slug,
title_link: build_url(event["region"], project, slug),
fallback: status,
fields: [
{
title: "Status",
value: status,
short: true,
},
{
title: "Initiator",
value: event["detail"]["additional-information"]["initiator"],
short: true,
},
],
},
],
}
end
def notify_slack(slack_url, payload)
Net::HTTP.post_form(URI.parse(slack_url), payload: JSON.dump(payload))
end
def lambda_handler(event:, context:)
slack_url = decrypt(ENV["ENCRYPTED_SLACK_WEBHOOK_URL"])
payload = {
channel: ENV["SLACK_CHANNEL"],
username: ENV["SLACK_USERNAME"],
icon_emoji: ENV["SLACK_EMOJI"],
}.merge(format(event))
notify_slack(slack_url, payload)
end
以上です。
AWSもTerraformも使い始めて日が浅いので、何か少しでも気になる部分があれば気軽にコメントやIssue作成をお願いします。