【AWS CDK】AWS CloudWatch AlarmsからのメッセージをGoogle Hangouts Chatに自動通知する構成を速攻で作る


AWS CloudWatch AlarmsからのメッセージをGoogle Hangouts Chatに通知したいことがあるかと思います。
今回はその構成をCDKで作成してみました。

環境

CDK CLI: 1.32.0
ローカルのNode: 11.15.0

構成

今回は、trigger-chat-cdkというLambda関数を監視対象とし、「1分間に2回以上、関数が呼び出されたらエラーを挙げる」という条件を設定します。

以下は手順になりますが、このリポジトリをcloneすれば、手順の3,4は省略できるので、より速攻で作れるかと思います。

1. Hangouts ChatのWebhook URLを作成する

まず前準備として、Hangouts ChatのWebhook URLを作成する必要があります。
作成は以下リンク先の「準備」を参照すれば可能です。
https://qiita.com/iitenkida7/items/3c8f9f8f6ee1e809558d#%E6%BA%96%E5%82%99

2. Webhook URLをパラメータストアに設定する

AWSマネジメントコンソールを開き、
System Manager→パラメータストア→パラメータの作成
と進み、

名前:HANGOUTS_CHAT_WEBHOOK_URL
値:取得したWebhook URL
種類:SecureString

で設定します。

3. CDKでスタックの作成

ここまでの準備ができたら、CDKのスタックを定義していきます。

lib/cdk-alarm-stack.ts

import * as cdk from '@aws-cdk/core'
import * as sns from '@aws-cdk/aws-sns'
import * as subs from '@aws-cdk/aws-sns-subscriptions'
import * as cw from '@aws-cdk/aws-cloudwatch'
import * as lambda from '@aws-cdk/aws-lambda'
import * as iam from '@aws-cdk/aws-iam'
import * as cw_actions from '@aws-cdk/aws-cloudwatch-actions'

export class CdkAlarmStack extends cdk.Stack {
  constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);
    const layer = new lambda.LayerVersion(this, 'node-fetch', {
      code: lambda.Code.asset('layer/forChat'),
      compatibleRuntimes: [
        lambda.Runtime.NODEJS_10_X,
        lambda.Runtime.NODEJS_12_X
      ],
      layerVersionName: 'node-fetch'
    })
    const triggerFunction = new lambda.Function(this, 'triggerFunction', {
      runtime: lambda.Runtime.NODEJS_12_X,
      handler: 'index.handler',
      code: lambda.Code.asset('lambda/trigger-chat-cdk'),
      functionName: 'trigger-chat-cdk'
    })
    const sendChatFunction = new lambda.Function(this, 'sendChatFunction', {
      runtime: lambda.Runtime.NODEJS_12_X,
      handler: 'index.handler',
      code: lambda.Code.asset('lambda/send-chat-cdk'),
      functionName: 'send-chat-cdk',
      layers: [
        layer
      ]
    })
    sendChatFunction.addToRolePolicy(new iam.PolicyStatement({
      actions: [
        'sts:AssumeRole',
        'ssm:GetParameter'
      ],
      resources: [
        '*'
      ]
    }))
    const topic = new sns.Topic(
      this,
      'sendChatTopic',
      {
        displayName: 'send chat',
        topicName: 'sendChatTopicCdk'
      }
    )
    topic.addSubscription(new subs.LambdaSubscription(sendChatFunction))
    const alarm = new cw.Alarm(this, 'sendChatAlarm', {
      evaluationPeriods: 1,
      metric: triggerFunction.metricInvocations(),
      threshold: 2,
      period: cdk.Duration.minutes(1),
      alarmName: 'sendChatCdk'
    })
    alarm.addAlarmAction(new cw_actions.SnsAction(topic));

  }
}


triggerFunctionを別のリソースに書き換えれば、アラームの監視対象を変えることができますし、alarmを書き換えれば、アラームの定義を自由に設定することができます。

4. Chatにメッセージを送るLambda関数を書く

今回はLambda関数として
- 監視対象となるtrigger-chat-cdk
- Chatにメッセージを送るsend-chat-cdk
の2つを定義する必要がありますが、trigger-chat-cdkの中身は正直なんでも良いので、説明を省略します。

send-chat-cdkの中身は以下の通りです。

send-chat-cdk.js
const fetch = require('node-fetch')
const AWS = require('aws-sdk')

exports.handler = async (event, context, callback) => {
  const ssm = new AWS.SSM()
  const res = await ssm.getParameter({ Name: 'HANGOUTS_CHAT_WEBHOOK_URL', WithDecryption: true }).promise()

  const fromSNS = event.Records[0].Sns.Message
  const data = JSON.stringify({
    text: `${fromSNS}`
  })

  fetch(res.Parameter.Value, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json; charset=UTF-8'
    },
    body: data
  })

  callback(null, event)
}

この関数で、node-fetchというモジュールをインポートしているので、このモジュールを使えるようにします。

5. Layerで使用するモジュールをインストールする

今回使用するモジュールはLayerにセットします。
そのため、lambda/layer/forChat/nodejsというディレクトリを作成し、このディレクトリで

$ npm i node-fetch

を実施し、インストールします。

6. デプロイ

ここまで完了したら、

$ cdk deploy

で、スタックをデプロイします。

7.実行テスト

AWSのマネジメントコンソールから、Lambdaのtrigger-chat-cdkの管理画面を開き、適当なテストイベントを2回以上実行します。

1分ほど待って、作成したHangouts ChatのチャットルームにCloudWatchからのエラーメッセージが届けば成功です!

メッセージ文の内容は、Lambdaの中身を適宜アレンジして頂ければと思います。

以上、ありがとうございました。