5
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

AWS DevOps AgentとCloudWatchアラームを連携して調査からレポート通知までを自動化する

5
Posted at

概要

CloudWatchアラームを起点としてAWS DevOpsエージェントによる調査を自動実行し、調査結果のレポートをメールで通知する一連の仕組みを構築します。

構成図は下記のとおりです。

d01.png

AWS DevOpsエージェントは一部のサードパーティ製品と統合したウェブフックをサポートしていますが、CloudWatchはこの対象に含まれていません。そのため、本記事では汎用ウェブフックを使用して連携を実現します。

CloudWatchアラーム発生時に呼び出されるLambda関数内で、HMAC署名付きのHTTPリクエストをDevOpsエージェントのウェブフックエンドポイントに送信します。DevOpsエージェントの調査完了後は、EventBridgeでステータス変化を検知し、後続のLambda関数からDevOpsエージェントのAPIを使用してレポートを取得、SNS経由でメール通知を行います。

なお、エージェントスペースの作成などDevOpsエージェントの基本的な設定は完了している前提とします。詳細は下記ブログを参照ください。

設定

ウェブフックの設定

DevOpsエージェントの設定画面からウェブフックを追加します。

d02.png

画面を進めると、URLとシークレットキーの生成画面が表示されます。これらは再取得できないため、必ずメモした上で追加をクリックします。

d03.png

下記のようにウェブフックが追加されます。

d04.png

DevOpsエージェント連携用Lambdaの設定

ランタイムにPython 3.12を選択し、Lambda関数を作成します。

d05.png

環境変数

先ほどメモしたURLとシークレットキーを環境変数に設定します。

キー
DEVOPS_AGENT_WEBHOOK_URL ウェブフックエンドポイントURL
DEVOPS_AGENT_WEBHOOK_SECRET シークレットキー

d06.png

タイムアウト

タイムアウトを1分に設定します。

d08.png

リソースベースポリシー

CloudWatchアラームからLambdaを直接呼び出すため、以下のリソースベースポリシーを追加します。

項目
Statement ID AllowCloudWatchAlarmInvoke
Principal lambda.alarms.cloudwatch.amazonaws.com
Action lambda:InvokeFunction

d07.png

コード

下記コードを貼り付け、デプロイをクリックします。

import base64
import hashlib
import hmac
import json
import os
import urllib.request
from datetime import datetime, timezone

WEBHOOK_URL = os.environ["DEVOPS_AGENT_WEBHOOK_URL"]
WEBHOOK_SECRET = os.environ["DEVOPS_AGENT_WEBHOOK_SECRET"]


def generate_signature(timestamp, payload):
    message = f"{timestamp}:{payload}"
    sig = hmac.new(
        WEBHOOK_SECRET.encode("utf-8"),
        message.encode("utf-8"),
        hashlib.sha256,
    ).digest()
    return base64.b64encode(sig).decode("utf-8")


def lambda_handler(event, context):
    alarm_name = event.get("alarmData", {}).get("alarmName", "Unknown")
    state = event.get("alarmData", {}).get("state", {})
    new_state = state.get("value", "UNKNOWN")
    reason = state.get("reason", "")
    region = event.get("region", "")
    account_id = event.get("accountId", "")

    if new_state != "ALARM":
        return {"statusCode": 200, "body": "Skipped"}

    now = datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%S.000Z")
    incident_id = f"cw-alarm-{alarm_name}-{int(datetime.now(timezone.utc).timestamp())}"

    payload_dict = {
        "eventType": "incident",
        "incidentId": incident_id,
        "action": "created",
        "priority": "HIGH",
        "title": f"CloudWatch Alarm: {alarm_name}",
        "description": f"Alarm '{alarm_name}' transitioned to ALARM. Reason: {reason}",
        "timestamp": now,
        "service": alarm_name,
        "data": {
            "metadata": {
                "region": region,
                "accountId": account_id,
                "alarmName": alarm_name,
                "newStateReason": reason,
            },
            "originalAlarm": event,
        },
    }

    payload = json.dumps(payload_dict)
    signature = generate_signature(now, payload)

    req = urllib.request.Request(
        WEBHOOK_URL,
        data=payload.encode("utf-8"),
        headers={
            "Content-Type": "application/json",
            "x-amzn-event-timestamp": now,
            "x-amzn-event-signature": signature,
        },
        method="POST",
    )

    with urllib.request.urlopen(req) as response:
        return {"statusCode": response.status}

SNSの作成

SNSトピックを作成し、メール通知用のサブスクリプションを追加します。詳細は割愛しますが、サブスクリプション作成後に届く確認メールのリンクをクリックし、ステータスが「確認済み」になっていることを確認してください。

レポート送信用Lambdaの設定

こちらもPython 3.12で作成します。

環境変数

キー
SNS_TOPIC_ARN 作成したSNSトピックのARN

d09.png

タイムアウト

タイムアウトを30秒に設定します。

IAMロール

検証目的のため、Lambdaの実行ロールに以下のマネージドポリシーを付与します。

  • AIDevOpsAgentFullAccess
  • AmazonSNSFullAccess

本番環境では最小権限の原則に従い、必要なアクションのみに絞ってください。

コード

下記コードを貼り付け、デプロイをクリックします。

import json
import os
import boto3

SNS_TOPIC_ARN = os.environ["SNS_TOPIC_ARN"]
sns_client = boto3.client("sns")
devops_client = boto3.client("devops-agent")


def get_investigation_summary(agent_space_id, execution_id):
    response = devops_client.list_journal_records(
        agentSpaceId=agent_space_id,
        executionId=execution_id,
        recordType="investigation_summary_md",
        limit=1,
        order="DESC",
    )
    records = response.get("records", [])
    if records:
        content = records[0].get("content", "")
        if isinstance(content, dict):
            return content.get("markdown", json.dumps(content, ensure_ascii=False))
        return str(content)
    return None


def lambda_handler(event, context):
    detail_type = event.get("detail-type", "DevOps Agent Event")
    detail = event.get("detail", {})
    metadata = detail.get("metadata", {})
    data = detail.get("data", {})

    priority = data.get("priority", "UNKNOWN")
    agent_space_id = metadata.get("agent_space_id", "")
    execution_id = metadata.get("execution_id", "")

    subject = f"[DevOps Agent] {detail_type} | {priority}"

    summary = None
    if detail_type == "Investigation Completed" and agent_space_id and execution_id:
        summary = get_investigation_summary(agent_space_id, execution_id)

    message = summary if summary else json.dumps(event, indent=2, ensure_ascii=False)

    sns_client.publish(
        TopicArn=SNS_TOPIC_ARN,
        Subject=subject[:100],
        Message=message[:262144],
    )

    return {"statusCode": 200}

このLambdaでは、DevOpsエージェントのAPIを使用して調査レポートを取得しています。

参考: JournalRecord APIリファレンス

下記の箇所で、agentSpaceIdexecutionIdはEventBridgeから連携されるイベントJSONのメタデータから取得し、recordTypeinvestigation_summary_mdを指定することでレポート本文を取得しています。

devops_client = boto3.client("devops-agent")

response = devops_client.list_journal_records(
    agentSpaceId="xxxxxxxxx",
    executionId="xxxxxxxxx",
    recordType="investigation_summary_md",
    limit=1,
    order="DESC",
)

EventBridgeの設定

以下のイベントパターンでEventBridgeルールを作成します。調査完了時のみ通知する場合はInvestigation Completedだけで十分ですが、検証のため他のステータスも含めています。

{
  "source": ["aws.aidevops"],
  "detail-type": [
    "Investigation Completed",
    "Investigation Failed",
    "Investigation Linked",
    "Investigation Timed Out"
  ]
}

ターゲットとして、先ほど作成したレポート送信用Lambdaを指定します。

検証

ここからはアラート発報から調査、レポート受信までの一連の流れを確認します。

CloudWatchアラームの作成

検証用に、CPU使用率が10%以下でアラート状態となるアラームを作成します。

d10.png

ターゲットとしてDevOpsエージェント連携用のLambdaを指定します。

d11.png

調査の確認

アラーム状態に遷移した後、DevOpsエージェントのWebコンソールからインシデントレスポンスダッシュボードを確認します。先ほどのアラート名で調査が開始されていることが確認できます。

d20.png

調査が完了すると、下記のようにレポートが表示されます。

d21.png

メール通知の確認

調査完了後、レポートがメールで送信されます。

# Investigation Summary

## Symptoms

### CloudWatchアラーム「devops-test」が繰り返しALARM状態に遷移
**Description:** CloudWatchアラーム「devops-test」が2026-xx-xx xx:xx:xxから繰り返しALARM状態に
遷移しています。アラームはEC2インスタンス i-xxxxxxxxxxxx のCPU使用率を監視しており、
閾値10.0%に対してCPU使用率が2.77-5.14%の範囲で推移している状態で、
約10分ごとにALARM状態とOK状態の間を往復しています。
これまでに少なくとも7回のALARM遷移が発生し、7件の重複調査が作成されています。
**Time:** 2026-xx-xxTxx:xx:xxZ

## Findings

### Cause: CloudWatchアラーム「devops-test」の比較演算子が意図的に頻繁に変更されている
**Description:** CloudWatchアラーム「devops-test」の比較演算子が、
LessThanOrEqualToThresholdとGreaterThanOrEqualToThresholdの間で繰り返し切り替えられています。
アラーム作成(xx:xx:xx)から現在(xx:xx:xx以降)までの約84分間で18回以上の設定変更が行われ、
平均4.5分に1回の頻度で設定が変更されています。EC2インスタンス i-xxxxxxxxxxxx のCPU使用率は
常に2.77-5.14%(閾値10.0%の半分以下)で推移しているため、LessThanOrEqualToThreshold設定時には
必ずALARM状態になり、GreaterThanOrEqualToThreshold設定時には必ずOK状態になります。
この規則的なパターンは、意図的な設定変更テスト以外では説明できません。
**Cascades to:** symptom-1

### Root Cause: テストアラーム「devops-test」に本番通知アクション(Lambda関数)が設定されている
**Description:** アラーム名が「devops-test」という明示的なテスト目的の名前であるにもかかわらず、
本番通知アクション(arn:aws:lambda:ap-northeast-1:xxxxxxxxxxxx:function:CloudWatch-DevOpsAgent)が
設定されており、ActionsEnabledがtrueになっています。
このため、テスト中のアラーム設定変更によるALARM状態遷移のたびに、
本番通知システムが起動し、調査が作成されます。結果として、約84分間で7件の重複した調査が生成され、
運用チームに誤った警告を送り続けています。テストアラームには本番通知アクションを設定しないか、
ActionsEnabledをfalseに設定すべきでした。
**Cascades to:** cause-1

※何度か検証を繰り返したため、上記ダッシュボードのレポートとメールの内容は異なる場合があります。

まとめ

CloudWatchアラームを起点に、DevOpsエージェントによる自動調査とレポートのメール通知の検証を行いました。CloudWatchはDevOpsエージェントの統合ウェブフック対象外ですが、Lambdaと汎用ウェブフックを組み合わせることで連携が可能です。本構成により、アラート発生から原因分析、レポート受信までを自動化でき、運用初動対応の負荷軽減が出来る可能性があります。

5
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?