Cloudformationで構築するCloudWatchアラームSlack連携

CloudWatchのアラームをSlackに通知したくて、 一撃で作成するCloudformationテンプレートがほしいなと思い書き始めたエントリです。どうせ書くなら、できるだけ汎用的にしたいと思い、Cloudformationのテンプレートを分けたり、システム固有の設定値(スタック名等)を環境変数したりとしていたら、いつの間にか手順が増えてしまいました...
2018.10.01

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

こんにちは、坂巻です。

CloudWatchのアラームをSlackに通知したくて、 一撃で作成するCloudformationテンプレートがほしいなと思い書き始めたエントリです。

どうせ書くなら、できるだけ汎用的にしたいと思い、Cloudformationのテンプレートを分けたり、システム固有の設定値(スタック名等)を環境変数したりとしていたら、いつの間にか手順が増えてしまいました...

本エントリの手順よりも、Blueprintを利用して作成した方が構築自体は早く済む感は否めないのですが、 書き始めてしまったので、備忘として残しておきたいと思います。

なお、Blueprintを利用した手順については、以下のエントリが参考になると思います。

Lambdaの「Blueprint」で簡単にSlackとCloudWatchを連携してみた(2017年版)

目次

  • 前提
  • 環境設定
  • デプロイパッケージ作成
  • スタック作成
  • 暗号化設定
  • 動作確認
  • さいごに

前提

  • Lmbdaソースを格納するS3バケットが作成済みであること
  • Lambdaに付与するロールが作成済みであること
  • KMSの権限(kms:Decrypt)がアタッチされていること
  • SNSトピックが作成済みであること
  • サブスクリプションの作成は、本設定の中で行います
  • Slack webhookURL、チャンネル名が取得済みであること

環境設定

Lambdaソースダウンロード

Slack通知用のスクリプト及び、Cloudformationテンプレートは以下に保存しています。

スクリプトは、Blueprint(2018年9月29日現在の、cloudwatch-alarm-to-slack-python3)と同様です。

変数設定

AWS CLIからスタック作成を実行していきますので、システム固有の値を変数に設定します

LAMBDA_SORCE_DIR=~/SlackTest/LambdaSorceCode/
CFN_TEMPLATE_DIR=~/SlackTest/CFnTemplate/
ZIP_NAME=lambda_to_slack.zip
KMS_TEMPLATE_NAME=Cfn_Template_KMS.yml
LAMBDA_TEMPLATE_NAME=Cfn_Template_Lambda.yml
SUBSCRIPTION_TEMPLATE_NAME=Cfn_Template_SNS.yml
KMS_STACK_NAME=Stack-KMS
LAMBDA_STACK_NAME=Stack-Lambda-to-Slack
SUBSCRIPTION_STACK_NAME=Stack-SNS-Subscription
KMS_KEY_ALIAS=LambdaToSlack
S3_BUCKET_NAME=slack-test-lambda-sorce-code
LAMBDA_FUNCTION_NAME=LambdaToSlack
LAMBDA_HANDLER_NAME=lambda_to_slack.lambda_handler
KMS_ENCRYPTED_HOOK_URL=hooks.slack.com/services/XXXXXXXX
ROLE_NAME=LambdaToSlackRole
SLACK_CHANEEL=#test
SNS_TOPIC_ARN=arn:aws:sns:ap-northeast-1:XXXXXXXX

変数に設定する値は環境にあわせて設定してください。

  • 実行環境設定
  • LAMBDA_SORCE_DIR…Lambdaソースコード格納ディレクトリ
  • CFN_TEMPLATE_DIR…Cloudformationのテンプレート格納ディレクトリ
  • ZIP_NAME…作成するLambdaデプロイパッケージ名
  • Cloudformation設定
  • KMS_TEMPLATE_NAME…KMS作成用のテンプレート名
  • LAMBDA_TEMPLATE_NAME…Lambda作成用のテンプレート名
  • SUBSCRIPTION_TEMPLATE_NAME…SNSサブスクリプション作成用のテンプレート名
  • KMS_STACK_NAME…KMS作成用のスタック名
  • LAMBDA_STACK_NAME…Lambda作成用のスタック名
  • SUBSCRIPTION_STACK_NAME…SNSサブスクリプション作成用のスタック名
  • KMS設定
  • KMS_KEY_ALIAS…KMSのカスタマーマスターキー(CMK)表示名
  • Lambda設定
  • S3_BUCKET_NAME…Lambda関数のソースコードが格納されたS3バケット
  • LAMBDA_FUNCTION_NAME…作成するLambdaの関数名
  • LAMBDA_HANDLER_NAME…作成するLambdaのハンドラー名
  • KMS_ENCRYPTED_HOOK_URL…SlackのWebhook URL
  • ROLE_NAME=Lambdaを実行するロール名
  • SLACK_CHANEEL=通知を行うSlackのチャンネル名
  • SNS_TOPIC_ARN=作成済みのSNSトピック名

変数確認

設定された変数が意図した通りに設定されているか確認しましょう。

cat << ETX
  LAMBDA_SORCE_DIR: ${LAMBDA_SORCE_DIR}
  CFN_TEMPLATE_DIR: ${CFN_TEMPLATE_DIR}
  ZIP_NAME: ${ZIP_NAME}
  KMS_TEMPLATE_NAME: ${KMS_TEMPLATE_NAME}
  LAMBDA_TEMPLATE_NAME: ${LAMBDA_TEMPLATE_NAME}
  SUBSCRIPTION_TEMPLATE_NAME: ${SUBSCRIPTION_TEMPLATE_NAME}
  KMS_STACK_NAME: ${KMS_STACK_NAME}
  LAMBDA_STACK_NAME: ${LAMBDA_STACK_NAME}
  SUBSCRIPTION_TEMPLATE_NAME: ${SUBSCRIPTION_TEMPLATE_NAME}
  KMS_KEY_ALIAS: ${KMS_KEY_ALIAS}
  S3_BUCKET_NAME: ${S3_BUCKET_NAME}
  LAMBDA_FUNCTION_NAME: ${LAMBDA_FUNCTION_NAME}
  LAMBDA_HANDLER_NAME: ${LAMBDA_HANDLER_NAME}
  KMS_ENCRYPTED_HOOK_URL: ${KMS_ENCRYPTED_HOOK_URL}
  ROLE_NAME: ${ROLE_NAME}
  SLACK_CHANEEL: ${SLACK_CHANEEL}
  SNS_TOPIC_ARN: ${SNS_TOPIC_ARN}
ETX

デプロイパッケージ作成

Lambda関数用のデプロイパッケージ (コード、依存関係等が構成されるzipファイル)を作成します。 Lambdaのソースコードが格納されたディレクトリに移動します。

cd ${LAMBDA_SORCE_DIR}

Lambdaソースコードが格納された対象のディレクトリを指定して、zipにします。こちらがデプロイパッケージとなります。なお、Lambdaには、Python向けのAWS SDK(Boto 3)が含まれます。デプロイパッケージに含める必要はありません。

zip -r ${ZIP_NAME} .

デプロイパッケージが作成されたことを確認しましょう。

ls -l ${ZIP_NAME}

デプロイパッケージアップロード

デプロイパッケージをS3バケットにアップロードします。

aws s3 cp ${ZIP_NAME} s3://${S3_BUCKET_NAME}/

デプロイパッケージが正常にアップロードできたか確認しましょう。

aws s3 ls s3://${S3_BUCKET_NAME}/

スタック作成

KMS

Slack通知用のLambda関数では、SlackのWebhook URLを暗号化していますので、データの暗号化に使用する暗号化キーを作成します。

aws cloudformation create-stack \
--stack-name ${KMS_STACK_NAME} \
--template-body file://"${CFN_TEMPLATE_DIR}${KMS_TEMPLATE_NAME}" \
--parameters ParameterKey=KmsKeyAlias,ParameterValue=${KMS_KEY_ALIAS} \
  ParameterKey=RoleName,ParameterValue=${ROLE_NAME}

スタックが正常に作成されたことを確認しましょう。

1

以下のようなカスタマーマスターキーが作成されます。

2

Lambda

Slack通知用のLambda関数を作成します。

aws cloudformation create-stack \
--stack-name ${LAMBDA_STACK_NAME} \
--template-body file://"${CFN_TEMPLATE_DIR}${LAMBDA_TEMPLATE_NAME}" \
--parameters ParameterKey=KmsStackName,ParameterValue=${KMS_STACK_NAME} \
ParameterKey=FunctionName,ParameterValue=${LAMBDA_FUNCTION_NAME} \
ParameterKey=Handler,ParameterValue=${LAMBDA_HANDLER_NAME} \
ParameterKey=S3BucketName,ParameterValue=${S3_BUCKET_NAME} \
ParameterKey=S3KeyName,ParameterValue=${ZIP_NAME} \
ParameterKey=slackChannel,ParameterValue=${SLACK_CHANEEL} \
ParameterKey=kmsEncryptedHookUrl,ParameterValue=${KMS_ENCRYPTED_HOOK_URL} \
ParameterKey=LambdaExecRole,ParameterValue=${ROLE_NAME} \
ParameterKey=SnsTopicName,ParameterValue=${SNS_TOPIC_ARN}

スタックが正常に作成されたことを確認しましょう。

3

以下のようなLambdaが作成されます。

4

暗号化設定

作成したLambda関数の環境変数に作成された、SlackのWebhook URLを暗号化します。 (Cloudformationで暗号化までする方法が見つからなかったので、マネジメントコンソールから実施しています。)

環境変数内の[暗号化の設定]を展開し、[伝送中の暗号化のためのヘルパーの有効化]にチェックを付与します。 [伝送中に暗号化する KMS キー]より、作成したKMSを指定します。

5

[暗号化]をクリックします。

6

暗号化されたことを確認して[保存]をクリックします。

7

サブスクリプション作成

Slack通知に必要な、Lambda関数用のサブスクリプションを作成します。

aws cloudformation create-stack \
--stack-name ${SUBSCRIPTION_STACK_NAME} \
--template-body file://"${CFN_TEMPLATE_DIR}${SUBSCRIPTION_TEMPLATE_NAME}" \
--parameters ParameterKey=LambdaStackName,ParameterValue=${LAMBDA_STACK_NAME} \
ParameterKey=SnsTopicArn,ParameterValue=${SNS_TOPIC_ARN}

スタックが正常に作成されたことを確認しましょう。

11

以下のようなサブスクリプションが作成されます。

10

動作確認

準備ができましたので、実際にCloudWatchでアラームを飛ばしてみます。 CloudWatchのアラーム通知はLambda関数で指定したSNSトピックに飛ぶようにしましょう。 CPU使用率で監視しているので、閾値を「0以上」等にしてわざとアラーム状態にします。

8

Slackへの通知が確認できました。

9

さいごに

前提条件が多く、当初ほしかった一撃で作成するテンプレートとは程遠くなってしまいました.. 既にSNSトピックが構築済みのシステムで、Slack通知を追加するような時には利用できるはずです。 とはいえ、コードで管理するような要件がなければ、マネジメントコンソールから実施した方が早そうですね。