Lambdar

主に技術やソフトウェアについて

AWS SNS トピック通知を Slack に流す Terraform モジュールを作った

可用性が重要な全ての Web インフラストラクチャにおいて,アラート通知は欠かせない. 幸い,AWS では CloudWatch によってモニタリングが,そして Simple Notification Service (SNS) によってそのアラート通知が簡単に行える. Amazon SNS では,Eメール通知だけでなく AWS Lambda を使った通知も可能であるため,ほとんどどのようなメッセージプラットフォーム宛でも通知を流せる.

勿論,Slack も例外ではない.

既に SNS トピック通知を Slack に流すライブラリは存在する. しかし,それを Terraform で管理したいときには少々の苦労を伴う.インフラストラクチャを Terraform で構築しているなら,SNS から Slack に通知を流すのに必要なリソースも全て Terraform の支配下に置きたくなるだろう. そこで,そのライブラリに少しばかり手を加え,必要なことを行ってくれる Terraform モジュールを作った.

本稿では,その aws-sns-slack-terraform モジュールについて説明する.

github.com

TL;DR

あなたの Terraform スクリプトに,以下のようなコードを追加すれば良い.

module "sns_to_slack" {
  source = "github.com/builtinnya/aws-sns-slack-terraform/module"

  slack_webhook_url = "hooks.slack.com/services/XXX/XXX/XXX"
  slack_channel_map = "{ \"topic-name\": \"#slack-channel\" }"
}

resource "aws_sns_topic" "test_topic" {
  name = "topic-name"
}

resource "aws_lambda_permission" "allow_lambda_sns_to_slack" {
  statement_id = "AllowSNSToSlackExecutionFromSNS"
  action = "lambda:invokeFunction"
  function_name = "${module.sns_to_slack.lambda_function_arn}"
  principal = "sns.amazonaws.com"
  source_arn = "${aws_sns_topic.test_topic.arn}"
}

resource "aws_sns_topic_subscription" "lambda_sns_to_slack" {
  topic_arn = "${aws_sns_topic.test_topic.arn}"
  protocol = "lambda"
  endpoint = "${module.sns_to_slack.lambda_function_arn}"
}

変更すべき箇所を以下に挙げる.

  • 通知したいチームの Webhook URL にする.
slack_webhook_url = "hooks.slack.com/services/XXX/XXX/XXX"
  • 通知させたいトピック名,通知を流したいチャンネル名に変更する.文字列の内容は JSON オブジェクト形式であり,複数のトピック-チャンネルマッピングを指定できる.
slack_channel_map = "{ \"topic-name\": \"#slack-channel\" }"
  • SNS トピックのリソース名,トピック名をまともにする.変更した場合は,source_arntopic_arn での指定も忘れずに変更すること.
resource "aws_sns_topic" "test_topic" {
  name = "topic-name"
}

以上を適用すれば設定は完了だ.詳細は GitHub リポジトリを参照してほしい.

動いているところ

f:id:builtinnya:20170518005219p:plain

上の画像は,GitHub リポジトリ に用意したサンプル(minimal)を使って CloudWatch アラーム通知を実際に行っている場面のスクリーンショットだ. CloudWatch アラームについての通知だけでなく,AutoScaling イベントなども整形・表示される.サポートされていないものについてはメッセージのみが表示される.

仕組み

f:id:builtinnya:20170518015757p:plain

上図は,通知システム全体の構成を示すものだ(図は draw.io で描いた).図で示される通り,以下の流れで通知が行われる.

  1. イベントソース(e.g. CloudWatch Alarms)から SNS トピックに通知が行われる
  2. SNS は,そのトピックの購読者である AWS Lambda のハンドラにイベントを渡す
  3. イベントを受け取った Lambda ハンドラ(robbwagoner/aws-lambda-sns-to-slack を少し変えたもの)は,イベントの内容を見てどのサービスのものかを特定し,サポートしているものであればメッセージを綺麗に整形して Slack に投稿する.そうでなければ単にトピックから通知されたメッセージをそのまま流す.

実際に Slack に投稿するプログラムは robbwagoner/aws-lambda-sns-to-slack をほとんどそのまま使っている.ただし,Terraform へのつなぎこみやカスタマイズ性を向上させるため,以下の変更を行った.

  • Webhook URL,Slack 投稿時に表示されるデフォルトのユーザ名,デフォルトのチャンネルを環境変数で指定できるようにした.
  • トピック-チャンネルのマッピングを環境変数で指定できるようにした.マッピングは JSON オブジェクト形式の文字列だ.AWS Lambda では環境変数にカンマを含む文字列を指定するとエラーが出る問題があるため,Terraform の base64encode 関数を使って Base64 形式にエンコードしておき,Lambda ハンドラ側でデコードすることでこの問題を回避している.Base64 はバイナリデータを ASCII 文字列で表すエンコーディングだが,今回のように使用できない文字列の制限を回避するためにも使われる.Eメールでの利用や,BASIC 認証を実現する Authorization ヘッダでの利用はよく知られている.

おわりに

本稿では,SNS トピック通知を Slack に投稿するシステムを簡単に構築できる Terraform モジュールについて述べた. ソースコードは GitHub 上で公開されている.Issue や Pull Request を歓迎する.