CloudWatchアラームをLambdaでGoogle Chatに投稿する

#概要

CloudWatch メトリクスアラームを使用し AWS リソースの監視を開始する上で、通知を Google Chat にも投稿する必要が出てきたため、Lambda ファンクションを使用し、 SNS をトリガーにして Google Chat 連携を行いました。
本記事では、AWS CDK を利用して SNS トピックの作成 および、lambda リソースの作成を行い、 SNS のテストトピックを発行し、 Google Chatに通知が来るまで確認してみたいと思います。
実際の運用では、監視対象の CloudWatch メトリクスアラーム と 上記で作成した SNSトピックの紐付けを行うことで、アラーム発砲から Google Chat 通知までの一連の流れを実現することができます。


#前提

・ Goole Chatのチャットルームを作成し、Webhook の URLを取得している
・ AWS CDK デプロイ環境が整っている

# AWS CDK にて SNS/Lambda リソースの作成 

実装は以下になります。
CDK context を利用して、 Webhook の URL を渡してあげて、 Lambda の環境変数に展開しています。。


```DevopsAlarmStack.ts
import * as cdk from "@aws-cdk/core";
import * as sns from "@aws-cdk/aws-sns";
import * as iam from "@aws-cdk/aws-iam";
import * as lambda from "@aws-cdk/aws-lambda";
import * as subscriptions from "@aws-cdk/aws-sns-subscriptions";

export class DevopsAlarmStack extends cdk.Stack {
  constructor(scope: cdk.App, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    const webhookURL = this.node.tryGetContext("webhookURL") || "";

    // lambda用ロール作成
    const lambdaIamRole = new iam.Role(
      this,
      "test-iam-role-for-lambda-devops-alarm",
      {
        roleName: "test-iam-role-for-lambda-devops-alarm",
        assumedBy: new iam.ServicePrincipal("lambda.amazonaws.com"),
        managedPolicies: [
          iam.ManagedPolicy.fromAwsManagedPolicyName(
            "service-role/AWSLambdaBasicExecutionRole"
          ),
        ],
      }
    );

    // SNS トピック作成
    const myTopic = new sns.Topic(this, "test-alarm-topic", {
      displayName: "test-alarm-topic",
      topicName: "test-alarm-topic",
    });

    // 通知用 lambda 作成
    const myFunction = new lambda.Function(
      this,
      "test-lambda-alarm-handler",
      {
        code: lambda.Code.fromAsset("../src", {
          exclude: ["*.ts", "cdk.out"],
        }),
        handler: "AlarmHandler.handler",
        functionName: "test-lambda-alarm-handler",
        runtime: lambda.Runtime.NODEJS_12_X,
        role: lambdaIamRole,
        environment: {
          HANGOUT_WEBHOOK_URL: `${webhookURL}`,
        },
      }
    );

    // SNS と lambda の紐付け
    myTopic.addSubscription(new subscriptions.LambdaSubscription(myFunction));
  }
}

```

# Lambda 内関数の実装

Lambda 内の実装は以下になります。
Lambda で展開された環境変数から　HANGOUT_WEBHOOK_URL を取得し、利用しています。
SNS から渡ってきたイベントを、見やすいように整形して、Google Chat Webhook API を利用し、通知してあげます。

```AlarmHandler.ts
const fetch = require("node-fetch");

const webhookURL = process.env.HANGOUT_WEBHOOK_URL;

export const handler = async (event: any) => {
  const text = get_text(get_message_from_event(event));

  console.log(text);
  await fetch(webhookURL, {
    method: "POST",
    headers: {
      "Content-Type": "application/json; charset=UTF-8",
    },
    body: JSON.stringify({
      text: text,
    }),
  }).then((response: any) => {
    console.log(response);
  });
};

function get_message_from_event(event: any) {
  return JSON.parse(event.Records[0].Sns.Message);
}

function get_text(message: any) {
  const alarm_name = "AlarmName" in message ? message["AlarmName"] : "";
  const old_state = "OldStateValue" in message ? message["OldStateValue"] : "";
  const new_state = "NewStateValue" in message ? message["NewStateValue"] : "";
  const new_state_reason =
    "NewStateReason" in message ? message["NewStateReason"] : "";
  return (
    "*" +
    alarm_name +
    ":* " +
    old_state +
    " ⟶ " +
    new_state +
    "\n```" +
    new_state_reason +
    "```"
  );
}

```

# SNS テストトピックの発行

AWS コンソールにて下記の json メッセージを発行します。 
[Amazon SNS] - [トピック] - [上記のCDKにて作成したトピックを選択] - [メッセージの発行]  

```message.json
{
    "AlarmName": "sample-error",
    "AlarmDescription": "sampleでエラーが発生しました。",
    "AWSAccountId": "xxxxxxxxxxxx",
    "NewStateValue": "ALARM",
    "NewStateReason": "Threshold Crossed: 1 datapoint [2.0 (29/11/17 01:09:00)] was greater than or equal to the threshold (1.0).",
    "StateChangeTime": "2017-11-29T01:10:32.907+0000",
    "Region": "Asia Pacific (Tokyo)",
    "OldStateValue": "OK",
    "Trigger": {
        "MetricName": "sample-metric",
        "Namespace": "LogMetrics",
        "StatisticType": "Statistic",
        "Statistic": "SUM",
        "Unit": null,
        "Dimensions": [],
        "Period": 60,
        "EvaluationPeriods": 1,
        "ComparisonOperator": "GreaterThanOrEqualToThreshold",
        "Threshold": 1,
        "TreatMissingData": "- TreatMissingData:                    NonBreaching",
        "EvaluateLowSampleCountPercentile": ""
    }
}
```
※ サンプルのJSONデータは、<https://qiita.com/onooooo/items/f59c69e30dc5b477f9fd> を参考にさせていただきました。

# Google Chat での確認

Google Chat に以下のような形で、通知を取得することができました。

![スクリーンショット 2020-12-15 18.36.01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/869437/93f19dd1-2aab-e3ad-976f-a1f00b04e917.png)

#まとめ

AWS CDK を利用して SNS トピックの作成 および、lambda リソースの作成を行い、 SNS のテストトピックを発行し、 Google Chatに通知が来るまでを確認してみました。普段 Google Chat を利用されている方は、AWSリソースに何か生じた際にすぐ通知が来てくれるので便利だと思います。