[AWS CDK] EventBridge API DestinationでGoogle ChatへCloudWatchアラームの通知をしてみた

[AWS CDK] EventBridge API DestinationでGoogle ChatへCloudWatchアラームの通知をしてみた

EventBridge API Destination便利
Clock Icon2025.05.14

Google Chatにも通知したい

こんにちは、のんピ(@non____97)です。

皆さんはAWS上で発生したイベントをGoogle Chatに通知したいなと思ったことはありますか? 私はあります。

SlackやTeamsへの通知については調べると、よく出てきます。

https://dev.classmethod.jp/articles/notify-slack-to-eventbridge-with-chatbot/

https://dev.classmethod.jp/articles/aws-chatbot-custom-notification/

https://dev.classmethod.jp/articles/eventbridge-teams/

Google Chatへの通知に関する記事もありにはしましたが、Lambda関数からWebhookでPOSTするような形がほとんどでした。

https://dev.classmethod.jp/articles/google-hangouts-chat-integration-with-aws-lambda/

指定したHTTPエンドポイントにPOSTするだけなら、EventBridge API Destinationでもできます。

https://dev.classmethod.jp/articles/event-bridge-api-destinations-with-slack/

せっかくなので、LambdaレスでGoogle Chatに通知をしてみました。

やってみた

検証環境の構成

検証環境の構成は以下のとおりです。

[AWS CDK] EventBridge API DestinationでGoogle ChatへCloudWatchアラームの通知をしてみた検証環境構成図.png

CloudWatch Alarmの状態遷移をEventBridge Ruleで拾い、EventBridge API DestinationでGoogle ChatのWebhook URLにPOSTします。

また、通知するメッセージの整形はEventBridge Input Transformerで行います。

Webhook URLの発行

まずはWebhook URLの発行です。

適当に通知先のチャンネルを用意しました。

アプリと統合-WebhookからWebhookを追加をクリックします。

1.aws-nortification_-_Chat.png

適当に名前をつけて保存します。

2.着信 Webhook.png

Webhook URLが発行されたことを確認します。

3.Webhookの作成確認.png

発行されたWebhook URLはSSM Parameter Storeに保存しておきます。

4.SSM Parameter Storeの作成.png

各種リソースのデプロイ

CloudWatchアラームやEventBridge API Destinationなどの各種リソースをデプロイします。

https://github.com/non-97/google-chat-aws-notification

デプロイはAWS CDKで行いました。使用したコードは以下リポジトリに保存しています。

少し補足します。

EventBridge API Destination周りは以下のとおりです。

./lib/construct/notification-construct.ts
    const destination = new cdk.aws_events.ApiDestination(this, "Destination", {
      connection,
      endpoint: cdk.aws_ssm.StringParameter.fromStringParameterAttributes(
        this,
        "Endpoint",
        {
          parameterName: "/google-chat/webhook/aws-notification",
        }
      ).stringValue,
      httpMethod: cdk.aws_events.HttpMethod.POST,
    });

    const role = new cdk.aws_iam.Role(this, "Role", {
      assumedBy: new cdk.aws_iam.ServicePrincipal("events.amazonaws.com"),
      inlinePolicies: {
        InvokeApiDestination: new cdk.aws_iam.PolicyDocument({
          statements: [
            new cdk.aws_iam.PolicyStatement({
              effect: cdk.aws_iam.Effect.ALLOW,
              actions: ["events:InvokeApiDestination"],
              resources: [destination.apiDestinationArn],
            }),
          ],
        }),
      },

先ほど用意したWebhook URLを保存しているSSM Parameter Storeを参照するようにしています。

なんとなくSecure Stringにしたかったのですが、Secure Stringを受け付けられるプロパティは決まっています。注意しましょう。

リソース プロパティタイプ プロパティ
AWS::DirectoryService::MicrosoftAD Password
AWS::DirectoryService::SimpleAD Password
AWS::ElastiCache::ReplicationGroup AuthToken
AWS::IAM::User LoginProfile Password
AWS::KinesisFirehose::DeliveryStream RedshiftDestinationConfiguration Password
AWS::OpsWorks::App Source Password
AWS::OpsWorks::Stack CustomCookbooksSource Password
AWS::OpsWorks::Stack RdsDbInstances DbPassword
AWS::RDS::DBCluster MasterUserPassword
AWS::RDS::DBInstance MasterUserPassword
AWS::Redshift::Cluster MasterUserPassword

抜粋 : Systems Manager Parameter Store から SecureString 値を取得する - AWS CloudFormation

通知するメッセージの組み立てに使用しているEventBridge Input Transformer周りは以下のとおりです。

./lib/construct/notification-construct.ts
    const inputTemplate = JSON.stringify({
      text:
        "👋🌎 こんにちは! AWSに関する通知だよ!!" +
        "以下のメッセージを確認してね 🌎👋",
      cards_v2: [
        {
          card: {
            header: {
              title: "CloudWatch Alarm State Change",
              image_url:
                "https://fonts.gstatic.com/s/i/short-term/release/googlesymbols/error/default/24px.svg",
            },
            sections: [
              {
                header:
                  "\u003ca href='https://<region>.console.aws.amazon.com/cloudwatch/home?region=<region>#alarmsV2:alarm/<alarmName>'\u003eCloudWatchアラーム情報\u003c/a\u003e\u003c/a\u003e",
                widgets: [
                  {
                    text_paragraph: {
                      text: "AWSアカウントID : <account>",
                    },
                  },
                  {
                    text_paragraph: {
                      text: "リージョン : <region>",
                    },
                  },
                  {
                    text_paragraph: {
                      text: "CloudWatchアラーム名 : <alarmName>",
                    },
                  },
                  {
                    text_paragraph: {
                      text: "CloudWatchアラームの説明 : <alarmDescription>",
                    },
                  },
                ],
              },
              {
                header: "現在の状態",
                widgets: [
                  {
                    text_paragraph: {
                      text: "状態 : <stateValue>",
                    },
                  },
                  {
                    text_paragraph: {
                      text: "理由 : <stateReason>",
                    },
                  },
                  {
                    text_paragraph: {
                      text: "時刻 : <stateTimestamp>",
                    },
                  },
                ],
              },
              {
                header: "前回の状態",
                widgets: [
                  {
                    text_paragraph: {
                      text: "状態 : <previousStateValue>",
                    },
                  },
                  {
                    text_paragraph: {
                      text: "理由 : <previousStateReason>",
                    },
                  },
                  {
                    text_paragraph: {
                      text: "時刻 : <previousStateTimestamp>",
                    },
                  },
                ],
              },
            ],
          },
        },
      ],
    });
.
.
(中略)
.
.
    const cfnRule = rule.node.defaultChild as cdk.aws_events.CfnRule;
    cfnRule.addPropertyOverride("Targets", [
      {
        Arn: destination.apiDestinationArn,
        Id: "AwsNotificationTarget",
        RoleArn: role.roleArn,
        InputTransformer: {
          InputTemplate: inputTemplate,
          InputPathsMap: {
            account: "$.account",
            region: "$.region",
            alarmName: "$.detail.alarmName",
            alarmDescription: "$.detail.configuration.description",
            stateValue: "$.detail.state.value",
            stateReason: "$.detail.state.reason",
            stateTimestamp: "$.detail.state.timestamp",
            previousStateValue: "$.detail.previousState.value",
            previousStateReason: "$.detail.previousState.reason",
            previousStateTimestamp: "$.detail.previousState.timestamp",
          },
        },
      },
    ]);

メッセージの本文はカードインターフェイスを使って組み立てています。

カードインターフェイスのドキュメントは以下のとおりです。

https://developers.google.com/workspace/chat/api/reference/rest/v1/cards?hl=ja

それぞれのプロパティの説明について詳細に記載があるため、まずはこちらを確認するのが良いでしょう。

text_paragraphにはHTML形式で入力します。

ただし、EventBridge Input Transformerのプレースホルダーは<>とHTMLの各種タグと相性が悪いです。

回避策として以下記事で紹介しているとおり、ユニコードエスケープシーケンス形式でHTMLタグを表現します。

https://dev.classmethod.jp/articles/amazon-eventbridge-microsoft-teams/

動作確認

動作確認をしてみましょう。

CloudWatchアラームはCreateTagが一回以上呼び出された時にアラートになるように組んでいます。

適当にEC2インスタンスタグを付与すると、以下のようにGoogle Chatに通知が来ました。

5.aws-nortification_-_Chat.png

アラーム名やAWSアカウントID、リージョン、現在の状態、時刻と各種情報が整理されていて良い感じですね。

CloudWatchアラーム情報のリンクをクリックすると、AWSマネジメントコンソールで対象CloudWatchアラームを確認することができます。良い感じです。

6.アラートに遷移.png

対象のCloudWatchアラームはデータポイントがPUTされていない場合は閾値を超過していないと判定するようにしています。

その後、しばらくするとOKになったことを知らせる通知が来ました。

7.aws-nortification_-_Chat_OK.png

CloudWatchアラーム情報のリンクからCloudWatchアラームを確認すると、確かにOKになっていますね。

8.アラートに遷移_OK.png

EventBridge API Destination便利

EventBridge API DestinationでGoogle ChatへCloudWatchアラームの通知をしてみました。

Lambda関数を用意する手間がかからない分、便利ですね。メッセージの整形で複雑なことをしないのであれば十分選択肢に入るのではないでしょうか。

今回は指定していませんが、レートリミットの設定も可能です。DLQと組み合わせて通知できなかったイベントがあれば後で流すこともしやすいのかなと思います。

この記事が誰かの助けになれば幸いです。

以上、クラウド事業本部 コンサルティング部の のんピ(@non____97)でした!

Share this article

facebook logohatena logotwitter logo

© Classmethod, Inc. All rights reserved.