DevOps Agentの調査結果を、Amazon Qでアラーム通知のスレッドに返してみた

DevOps Agentの調査結果を、Amazon Qでアラーム通知のスレッドに返してみた

DevOps AgentのSlackアプリは調査の途中経過まで投稿するうえ、どのアラームの調査かも探しにくくなります。そこでSlackアプリを使わず、Amazon Qのカスタム通知で、調査の開始と結果だけをアラーム通知のスレッドに返してみました。
2026.10.30

こんにちは、製造ビジネステクノロジー部のはすとです。

前回は、Slackのアラーム通知に付けたボタンから、Lambda経由でAWS DevOps Agentの調査を起動する仕組みを作りました。

https://dev.classmethod.jp/articles/hasuto-devops-agent-slack-button-lambda/

ただ、DevOps AgentのSlackアプリは調査の途中経過も1件ずつ投稿するため、スレッド内の投稿が多くなり読むのが辛いです。また、新しいメッセージとして始まるため、どのアラームの調査なのかを、アラーム通知側から探す必要がありました。

そこで、DevOps AgentのSlack連携は使わず、Amazon Q Developer in chat applications(旧AWS Chatbot、以下Amazon Q)だけで、調査の開始と結果をアラーム通知のスレッドにまとめてみることにしました。

本記事では、上記の形にするまでに詰まった点を、再現しながらまとめます。

構成

アラーム通知から調査の結果が届くまでの流れは、以下のようになります。

CloudWatchアラームの通知のボタンからDevOps Agentの調査を起動し、調査が終わるとEventBridgeからLambdaが要約を読み、同じSNSトピックを通ってアラーム通知のスレッドに投稿される構成

  • 調査の開始(①〜⑤):ボタンを押すと、Amazon QのカスタムアクションがLambdaを呼び、Lambdaが調査を作ります。Lambdaの戻り値は、Amazon Qがボタンへの返事としてスレッドに投稿します
  • 調査の結果(⑥〜⑨):調査が終わるとDevOps AgentがEventBridgeにイベントを出し、Lambdaが要約を読んでアラーム通知と同じSNSトピックに送ります

ポイントは、結果を新しいトピックではなく、アラーム通知と同じトピックに送るところです。同じトピックを購読しているAmazon Qのチャンネル設定が、アラーム通知と同じチャンネルに結果を投稿します。

検証用の環境

  • リージョン: 東京(ap-northeast-1)
  • AWS CDK: aws-cdk-lib 2.274.0
  • Lambda: Node.js 24

CDKで、SNSトピック、アラーム、2つのLambda、EventBridgeのルール、Amazon Qのチャンネル設定を作ります。アラームのALARMとOKの通知を、カスタム通知と同じトピックに送ります。

lib/alarm-thread-stack.ts
import * as cdk from "aws-cdk-lib";
import * as chatbot from "aws-cdk-lib/aws-chatbot";
import * as cloudwatch from "aws-cdk-lib/aws-cloudwatch";
import * as actions from "aws-cdk-lib/aws-cloudwatch-actions";
import * as events from "aws-cdk-lib/aws-events";
import * as targets from "aws-cdk-lib/aws-events-targets";
import * as iam from "aws-cdk-lib/aws-iam";
import * as lambda from "aws-cdk-lib/aws-lambda";
import { NodejsFunction } from "aws-cdk-lib/aws-lambda-nodejs";
import * as sns from "aws-cdk-lib/aws-sns";
import { Construct } from "constructs";

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

    const slackWorkspaceId = this.node.getContext("slackWorkspaceId");
    const slackChannelId = this.node.getContext("slackChannelId");
    const agentSpaceId = this.node.getContext("agentSpaceId");
    const agentSpaceArn = `arn:aws:aidevops:${this.region}:${this.account}:agentspace/${agentSpaceId}`;

    // アラーム通知とカスタム通知の両方を、このトピックに送る
    const topic = new sns.Topic(this, "Topic", {
      topicName: "alarm-notification-topic",
    });

    // 手で ALARM にする用のアラーム。データが無いと OK に戻る
    const alarm = new cloudwatch.Alarm(this, "Alarm", {
      alarmName: "sample-alarm",
      metric: new cloudwatch.Metric({
        namespace: "Sample/App",
        metricName: "Errors",
        period: cdk.Duration.minutes(1),
        statistic: "Sum",
      }),
      threshold: 1,
      evaluationPeriods: 1,
      comparisonOperator: cloudwatch.ComparisonOperator.GREATER_THAN_OR_EQUAL_TO_THRESHOLD,
      treatMissingData: cloudwatch.TreatMissingData.NOT_BREACHING,
    });
    alarm.addAlarmAction(new actions.SnsAction(topic));
    alarm.addOkAction(new actions.SnsAction(topic));

    // ボタンから呼ばれ、DevOps Agent の調査を作る
    const investigateFn = new NodejsFunction(this, "InvestigateFunction", {
      entry: "lambda/investigate/index.mjs",
      runtime: lambda.Runtime.NODEJS_24_X,
      timeout: cdk.Duration.seconds(10),
      environment: { AGENT_SPACE_ID: agentSpaceId },
      // DevOps Agent のクライアントは新しいため、実行環境の SDK に頼らずまとめて入れる
      bundling: { externalModules: [] },
    });
    investigateFn.addToRolePolicy(
      new iam.PolicyStatement({ actions: ["aidevops:CreateBacklogTask"], resources: [agentSpaceArn] }),
    );
    investigateFn.addToRolePolicy(new iam.PolicyStatement({ actions: ["cloudwatch:DescribeAlarms"], resources: ["*"] }));

    // 調査が終わったら、要約をアラーム通知と同じトピックに送る
    const summaryFn = new NodejsFunction(this, "SummaryFunction", {
      entry: "lambda/summary/index.mjs",
      runtime: lambda.Runtime.NODEJS_24_X,
      timeout: cdk.Duration.seconds(30),
      environment: { TOPIC_ARN: topic.topicArn },
      bundling: { externalModules: [] },
    });
    summaryFn.addToRolePolicy(
      new iam.PolicyStatement({
        actions: ["aidevops:GetBacklogTask", "aidevops:ListJournalRecords"],
        resources: [agentSpaceArn],
      }),
    );
    topic.grantPublish(summaryFn);

    // 同じ source で対処(Mitigation)のイベントも届くため、調査の終わりの 3 つに絞る
    new events.Rule(this, "InvestigationEndRule", {
      eventPattern: {
        source: ["aws.aidevops"],
        detailType: ["Investigation Completed", "Investigation Failed", "Investigation Timed Out"],
      },
      targets: [new targets.LambdaFunction(summaryFn)],
    });

    // チャンネルロール: 通知の表示に使う読み取りと、ボタンの Lambda の呼び出しだけ
    const channelPermissions = [
      new iam.PolicyStatement({ actions: ["lambda:InvokeFunction"], resources: [investigateFn.functionArn] }),
      new iam.PolicyStatement({
        actions: ["cloudwatch:Describe*", "cloudwatch:Get*", "cloudwatch:List*"],
        resources: ["*"],
      }),
    ];
    const chatbotRole = new iam.Role(this, "ChatbotRole", {
      assumedBy: new iam.ServicePrincipal("chatbot.amazonaws.com"),
      inlinePolicies: { ButtonOnly: new iam.PolicyDocument({ statements: channelPermissions }) },
    });
    const guardrail = new iam.ManagedPolicy(this, "ChatbotGuardrail", { statements: channelPermissions });

    // ALARM の通知に「調査を依頼」のボタンを出す
    const investigateAction = new chatbot.CfnCustomAction(this, "InvestigateAction", {
      actionName: "alarm-thread-request-investigation",
      definition: {
        commandText: `lambda invoke --function-name ${investigateFn.functionName} --payload '{"alarmName":"$AlarmName"}' --region ${this.region}`,
      },
      attachments: [
        {
          notificationType: "CloudWatch",
          buttonText: "DevOps Agent に調査を依頼",
          variables: { AlarmName: "event.AlarmName", State: "event.NewStateValue" },
          criteria: [
            { variableName: "AlarmName", operator: "HAS_VALUE" },
            { variableName: "State", operator: "EQUALS", value: "ALARM" },
          ],
        },
      ],
    });

    new chatbot.CfnSlackChannelConfiguration(this, "SlackChannel", {
      configurationName: "alarm-channel",
      iamRoleArn: chatbotRole.roleArn,
      slackWorkspaceId,
      slackChannelId,
      snsTopicArns: [topic.topicArn],
      guardrailPolicies: [guardrail.managedPolicyArn],
      customizationResourceArns: [investigateAction.attrCustomActionArn],
      loggingLevel: "INFO",
    });

    new cdk.CfnOutput(this, "TopicArn", { value: topic.topicArn });
    new cdk.CfnOutput(this, "AlarmArn", { value: alarm.alarmArn });
  }
}

ボタンのカスタムアクションとチャンネルロールの考え方は、前回の記事と同じです。チャンネルロールには、ボタンのLambdaを呼ぶ権限と、通知の表示に使う読み取りの権限だけを付けています。

https://dev.classmethod.jp/articles/hasuto-devops-agent-slack-button-lambda/

スタックは東京リージョンに置きます。

bin/app.ts
import * as cdk from "aws-cdk-lib";
import { AlarmThreadStack } from "../lib/alarm-thread-stack";

const app = new cdk.App();

new AlarmThreadStack(app, "AlarmThreadStack", {
  env: { region: "ap-northeast-1" },
});

SlackのワークスペースID、チャンネルID、Agent SpaceのIDは、cdk.json の context に書きます。値は自分の環境のものに書き換えてください。

cdk.json
{
  "app": "pnpm exec ts-node --prefer-ts-exts bin/app.ts",
  "context": {
    "slackWorkspaceId": "<ワークスペース ID>",
    "slackChannelId": "<チャンネル ID>",
    "agentSpaceId": "<Agent Space ID>"
  }
}

チャンネルには、先に /invite @Amazon Q でAmazon Qを招待しておきます。そのうえでデプロイします。

pnpm exec cdk deploy
Outputs:
AlarmThreadStack.AlarmArn = arn:aws:cloudwatch:ap-northeast-1:123456789012:alarm:sample-alarm
AlarmThreadStack.TopicArn = arn:aws:sns:ap-northeast-1:123456789012:alarm-notification-topic

調査の開始をスレッドに返す

ボタンから呼ばれるLambdaです。前回の記事と同じく CreateBacklogTask で調査を作ります。戻り値の文字列は、Amazon Qがボタンへの返事としてスレッドに投稿します。

https://dev.classmethod.jp/articles/hasuto-devops-agent-slack-button-lambda/

lambda/investigate/index.mjs
import { CloudWatchClient, DescribeAlarmsCommand } from "@aws-sdk/client-cloudwatch";
import { CreateBacklogTaskCommand, DevOpsAgentClient } from "@aws-sdk/client-devops-agent";

const cloudwatch = new CloudWatchClient({});
const devopsAgent = new DevOpsAgentClient({});
const agentSpaceId = process.env.AGENT_SPACE_ID;

// 要約の Lambda は、題名からこの文字列を外してアラーム名にする
const TITLE_PREFIX = "Slack から調査依頼: ";

// ボタンから呼ばれる。戻り値の文字列は、Amazon Q がボタンへの返事としてスレッドに投稿する
export const handler = async (event) => {
  const alarmName = event?.alarmName;
  // 今 ALARM のものだけ調査する
  const { MetricAlarms = [] } = await cloudwatch.send(new DescribeAlarmsCommand({ AlarmNames: [alarmName] }));
  const alarm = MetricAlarms[0];
  if (alarm?.StateValue !== "ALARM") {
    return `${alarmName} は今 ALARM ではないため、調査を始めません。`;
  }

  await devopsAgent.send(
    new CreateBacklogTaskCommand({
      agentSpaceId,
      taskType: "INVESTIGATION",
      priority: "MEDIUM",
      title: `${TITLE_PREFIX}${alarmName}`,
      description: `Slack のアラーム通知のボタンから調査の依頼がありました。対象は CloudWatch アラーム ${alarmName} です。アラームの理由: ${alarm.StateReason}`,
    }),
  );
  return `調査を依頼しました(アラーム: ${alarmName})。結果はこのスレッドに投稿されます。`;
};

調査の題名は「Slack から調査依頼: <アラーム名>」にしています。結果を送るLambdaは、この題名からアラーム名を取り出してスレッドを決めます。

調査の終わりを受け取る

DevOps Agentは、調査の状態が変わるとEventBridgeの既定のイベントバスにイベントを出します。source は aws.aidevops で、detail-type で何のイベントかが分かります。調査が終わったときは Investigation Completed、失敗と時間切れは Investigation Failed と Investigation Timed Out です。

ただし、イベントには要約の本文が入っていません。公式ドキュメントには次のように書かれています。

The event does not contain the summary text. To read it, call ListJournalRecords with the event's metadata.agent_space_id and metadata.execution_id, a recordType of investigation_summary_md, and select the returned record whose recordId matches this value.

AWS DevOps Agent events detail reference - AWS DevOps Agent

イベントには要約の本文は入っておらず、読むには ListJournalRecords で調査の記録を取り出す、という意味です。

また、同じ source で、対処(Mitigation)のイベントや、調査が始まったときのイベントも届きます。ルールの detailType を、調査の終わりの3つに絞っておきます。

要約を読む

イベントで呼ばれるLambdaです。GetBacklogTask で調査の題名を読み、ボタンから起動した調査かを確かめます。CreateBacklogTask で作った調査は起動元(reference)が空のため、題名の書き出しだけで見分けています。

lambda/summary/index.mjs
import { DevOpsAgentClient, GetBacklogTaskCommand, ListJournalRecordsCommand } from "@aws-sdk/client-devops-agent";
import { PublishCommand, SNSClient } from "@aws-sdk/client-sns";

const devopsAgent = new DevOpsAgentClient({});
const sns = new SNSClient({});
const topicArn = process.env.TOPIC_ARN;

const TITLE_PREFIX = "Slack から調査依頼: ";

// DevOps Agent の調査が終わったときのイベント(EventBridge)で呼ばれる
export const handler = async (event) => {
  const { agent_space_id: agentSpaceId, task_id: taskId } = event.detail.metadata;

  // ボタンから起動した調査だけを扱う。題名の末尾がアラーム名
  const { task } = await devopsAgent.send(new GetBacklogTaskCommand({ agentSpaceId, taskId }));
  if (!task.title.startsWith(TITLE_PREFIX)) {
    return;
  }
  const alarmName = task.title.slice(TITLE_PREFIX.length);

  const link = `<https://${agentSpaceId}.aidevops.global.app.aws/investigation/${taskId}|調査の詳細を見る>`;
  let description = `${link}\n\n${event["detail-type"]}`;
  if (event["detail-type"] === "Investigation Completed") {
    const summary = await readSummary(agentSpaceId, task.executionId);
    description = [
      link,
      `*インシデント*\n${summary.incident ?? "(未記載)"}`,
      `*原因*\n${summary.cause ?? "(未記載)"}`,
      `*対応*\n${summary.mitigation ?? "(未記載)"}`,
    ].join("\n\n");
  }

  const message = {
    version: "1.0",
    source: "custom",
    content: {
      textType: "client-markdown",
      title: "DevOps Agent の調査結果",
      description: description.slice(0, 8000), // description の上限
    },
    metadata: {
      // アラーム通知のスレッドに入れる
      threadId: `arn:aws:cloudwatch:${event.region}:${event.account}:alarm:${alarmName}`,
      enableCustomActions: false,
    },
  };
  await sns.send(new PublishCommand({ TopicArn: topicArn, Message: JSON.stringify(message) }));
};

// Web App の「概要」タブの中身(調査中に何度も書き直される)の、最後の 1 件から 3 項目を取り出す
async function readSummary(agentSpaceId, executionId) {
  const { records } = await devopsAgent.send(
    new ListJournalRecordsCommand({
      agentSpaceId,
      executionId,
      recordType: "ui_investigation_summary",
      order: "DESC",
      limit: 1,
    }),
  );
  const texts = new Map();
  const walk = (node) => {
    if (typeof node?.id === "string" && typeof node.text === "string") {
      texts.set(node.id, node.text);
    }
    node?.children?.forEach(walk);
  };
  walk(JSON.parse(records[0].content).content);
  return {
    incident: texts.get("summary__incident"),
    cause: texts.get("summary__cause"),
    mitigation: texts.get("summary__mitigation"),
  };
}

要約は、記録の種類 ui_investigation_summary から読んでいます。この記録には、DevOps AgentのWeb Appの「概要」タブの中身が入れ子のJSONで入っており、「インシデント」「原因」「対応」の3項目をそのまま取り出せます。
ただ、ui_investigation_summary と、その中の summary__incident などのIDは公式ドキュメントに明記されていないため、変わるおそれはあります。

アラーム通知のスレッドに入れる

ここが一番詰まったところです。カスタム通知の threadId について、公式ドキュメントには次のように書かれています。

Custom notifications with the same threadId value are grouped together.

Custom notifications using Amazon Q Developer in chat applications - Parameter details

同じ threadId を持つカスタム通知は、1つのスレッドにまとめられる、という意味です。ただ、Amazon Qが投稿するCloudWatchアラームの通知にどんな threadId が付いているのかは、ドキュメントには書かれていませんでした。

そこで、カスタム通知を手で送るスクリプトを用意し、動かしてみました。
第1引数が threadId、第2引数がタイトルです。タイトルの先頭には、Slackでどの通知か見分けられるよう番号を付けています。

scripts/publish.sh
#!/usr/bin/env bash
# threadId を付けたカスタム通知を、アラーム通知と同じトピックに送る
# 使い方: scripts/publish.sh <threadId> <タイトル>
set -euo pipefail

THREAD_ID="$1"
TITLE="$2"
TOPIC_ARN=$(aws sns list-topics --query "Topics[?ends_with(TopicArn, ':alarm-notification-topic')].TopicArn" --output text)

MESSAGE=$(cat <<EOF
{
  "version": "1.0",
  "source": "custom",
  "content": {
    "textType": "client-markdown",
    "title": "${TITLE}",
    "description": "threadId: ${THREAD_ID}"
  },
  "metadata": {
    "threadId": "${THREAD_ID}",
    "enableCustomActions": false
  }
}
EOF
)

aws sns publish --topic-arn "$TOPIC_ARN" --message "$MESSAGE"

アラームは、set-alarm-state で手でALARMにします。メトリクスのデータが無いため、1分ほどでOKに戻り、OKの通知も届きます。

aws cloudwatch set-alarm-state --region ap-northeast-1 --alarm-name sample-alarm --state-value ALARM --state-reason "検証のため手で ALARM にする"

threadIdをそろえても、スレッドに入らない

今までの説明で、threadId をアラーム通知と同じ値にすれば、スレッドに入るはずと思いきや、スレッド内に全く入らず、少し詰まりました。どのALARM通知のスレッドには入らず、別のメッセージとしてチャンネルに投稿されてしまいます。

原因はチャンネル側の設定で、ドキュメントの冒頭にも、次の注意書きがありました。

To thread notifications, your channel preferences must allow sending notification updates in a thread. You can update your channel preferences by entering @Amazon Q preferences in your chat channel.

Custom notifications using Amazon Q Developer in chat applications

通知をスレッドにまとめるには、チャンネルの設定で、更新をスレッドに送ることを許しておく必要がある、という意味です。設定はチャンネルで @Amazon Q preferences と入力すると変えられます。

使っていたチャンネルは、この設定が「Display notification updates in main channel」になっていました。

main channel の設定では、カスタム通知がスレッドに入らずチャンネルに出る

@Amazon Q preferences を送ると、Amazon Qが「Set Preferences」のボタンが返ってきます。
このボタンを押すと、次のダイアログが開きます。

Channel preferences のダイアログ

選べるのは次の3つです。

  • Display notification updates in threads:更新をスレッドにまとめる
  • Display notification updates in main channel:スレッドにまとめず、チャンネルに出す
  • Display notification updates in threads and main channel:スレッドにまとめたうえで、チャンネルにも出す

「threads and main channel」にすると、通知がスレッドに入ったうえで、チャンネルにも同じ通知がでます。

その下の「Start new thread after」は、新しいスレッドに切り替えるまでの期間です。2 hours・8 hours・1 day・2 days・1 weekから選べ、既定は2 daysでした。

スレッドに入らないときは、まずこの設定を確かめましょう。

threadIdにはアラーム名ではなくARNを入れる

設定をスレッドにしたうえで、threadId に何を入れればアラーム通知のスレッドに入るかを試しました。候補は、アラームを指す値として思いつくアラーム名とARNの2つです。

アラームをALARMにしてから、2件のカスタム通知を送りました。

scripts/publish.sh arn:aws:cloudwatch:ap-northeast-1:123456789012:alarm:sample-alarm "1/2 threadId=アラームの ARN"
scripts/publish.sh sample-alarm "2/2 threadId=アラーム名"

ARNを入れた通知は、OKの通知と一緒にアラーム通知のスレッドに入りました。アラーム名を入れた通知は、スレッドに入らずチャンネルに出ました。

ARN の通知はスレッドに入り、アラーム名の通知はチャンネルに出る

試した範囲では、Amazon Qはアラーム通知をアラームのARNでまとめていると見られます。また、スレッドの元のメッセージには、スレッドに入ったカスタム通知の本文が「Latest Update」として表示されていました。ドキュメントにも、スレッドに新しいメッセージが入ると、元のメッセージに Latest Update を付けると書かれています。

To indicate new messages in a thread, we add a Latest Update message to the thread's parent message.

Custom notifications using Amazon Q Developer in chat applications - Custom notification content guidelines

要約を送るLambdaでも、アラーム名からARNを組み立てて threadId に入れています。

時間がたつと、新しいスレッドになる

「Start new thread after」の期間を過ぎてから同じ threadId で送ると、どうなるかも確かめました。ドキュメントには、次のように書かれています。

We attempt to group messages with the same threadId into a thread based on your threading time window preferences.

Custom notifications using Amazon Q Developer in chat applications - Custom notification content guidelines

同じ threadId のメッセージは、スレッドの期間の設定に基づいて1つのスレッドにまとめるよう試みる、という意味です。

期間を2 hoursにして、20:13にアラームをALARMにしました。直後に、ARNを threadId にしたカスタム通知を送りました。

scripts/publish.sh arn:aws:cloudwatch:ap-northeast-1:123456789012:alarm:sample-alarm "4 threadId=アラームの ARN(ALARM の直後)"

このカスタム通知は、20:14のOKの通知とあわせてALARMの通知のスレッドに入りました。そこから2時間以上たった22:20と22:21に、同じARNで2件送りました。

scripts/publish.sh arn:aws:cloudwatch:ap-northeast-1:123456789012:alarm:sample-alarm "5 threadId=アラームの ARN(2 時間後の 1 件目)"
scripts/publish.sh arn:aws:cloudwatch:ap-northeast-1:123456789012:alarm:sample-alarm "6 threadId=アラームの ARN(2 時間後の 2 件目)"

22:20に送った1件目は、20:13のスレッドには入らず、新しいメッセージとしてチャンネルに出ました。22:21に送った2件目は、その1件目のスレッドに入り、1件目に「Latest Update」が付きました。20:13のスレッドの返信は2件のままでした。

期間を過ぎた通知は新しいメッセージとしてチャンネルに出て、次の通知はそのスレッドに入る

つまり、期間を過ぎると、同じ threadId の通知から新しいスレッドが始まります。アラームの通知から時間がたってから調査を依頼すると、結果は元のアラーム通知のスレッドには入りません。既定の2 daysより後に調査することがあるなら、期間を長くしておくとよさそうです。

期間は、スレッドの最後の投稿からではなく、スレッドが始まった時刻から数えていると見られます。Amazon Qのログ(バージニア北部の /aws/chatbot/<チャンネル設定の名前>、loggingLevel を INFO にすると残ります)に、スレッドに送るときの記録が残っていました。

aws logs tail /aws/chatbot/alarm-channel --region us-east-1 --since 1h --format short | grep "Sending message"

実行結果です(時刻はUTC)。

2026-10-28T02:11:25 Sending message to existing Slack thread started at approx 2026-10-27T13:20:24.993Z (Workspace: <ワークスペース ID>, Channel: alarm-notify) with title: 🚨 CloudWatch Alarm | sample-alarm | ap-northeast-1 | Account: 123456789012
2026-10-28T02:19:24 Sending message to Slack (Workspace: <ワークスペース ID>, Channel: alarm-notify) with title: ✅ CloudWatch Alarm | sample-alarm | ap-northeast-1 | Account: 123456789012

翌日の11:11(UTCの02:11)のALARMの通知は、前日22:20の1件目から始まったスレッドに送られています。このときは、検証の途中でチャンネル設定を作り直したため、期間が既定の2 daysに戻っていました。そこで期間を2 hoursに変えると、11:19(UTCの02:19)のOKの通知は、新しいメッセージとして送られました。このスレッドには8分前にALARMの通知が入ったばかりでしたが、スレッドが始まった22:20からは2時間を過ぎていたため、と見られます。

実際に動かしてみる

ボタンのLambdaは、今ALARMのアラームだけを調査します。set-alarm-state で手でALARMにすると1分ほどでOKに戻るため、ここではメトリクスのデータを30秒ごとに送り、ALARMのままにしました。

for i in $(seq 1 20); do
  aws cloudwatch put-metric-data --region ap-northeast-1 --namespace Sample/App --metric-name Errors --value 5
  sleep 30
done

13:33にALARMの通知が届きました。通知の「DevOps Agent に調査を依頼」を押し、確認のダイアログで「Run」を押すと、Lambdaの戻り値がボタンへの返事としてスレッドに出ました。

ALARMの通知のスレッドに、Lambdaの戻り値「調査を依頼しました」が返事として出ている

調査は7分ほどで終わり、調査の結果がALARMの通知のスレッドに入りました。

ALARMの通知のスレッドに、開始の返事に続いて、インシデント・原因・対応をまとめた調査の結果が入っている

調査について投稿されるのは、開始の返事と結果の2件だけです。また、親のメッセージには「Latest Update」として結果の書き出しが付きました。ただし、description の先頭にリンクを置いているため、書き出しは <https://... のようにリンクの書式のまま表示されていました。

まとめ

DevOps AgentのSlack連携を使わなくても、調査の終わりのイベントとAmazon Qのカスタム通知で、調査の結果をアラーム通知のスレッドに返せました。結果は、threadId にアラームのARNを入れ、アラーム通知と同じSNSトピックに送ります。あわせて、チャンネルの @Amazon Q preferences が、通知をスレッドにまとめる設定になっている必要があります。また、「Start new thread after」の期間を過ぎると、同じ threadId でも新しいスレッドになります。

一番詰まったのは、threadId の値ではなくチャンネルの設定でした。値を変えても入らないときは、同じアラームのOKの通知がALARMの通知のスレッドに入っているかを見ると、設定の問題かどうかを切り分けられます。

私と同じように、アラームの通知と調査の結果を一箇所で見たい方の参考になれば嬉しいです。

参考

この記事をシェアする

AWSのお困り事はクラスメソッドへ

関連記事