概要
CloudWatchアラームを起点としてAWS DevOpsエージェントによる調査を自動実行し、調査結果のレポートをメールで通知する一連の仕組みを構築します。
構成図は下記のとおりです。
AWS DevOpsエージェントは一部のサードパーティ製品と統合したウェブフックをサポートしていますが、CloudWatchはこの対象に含まれていません。そのため、本記事では汎用ウェブフックを使用して連携を実現します。
CloudWatchアラーム発生時に呼び出されるLambda関数内で、HMAC署名付きのHTTPリクエストをDevOpsエージェントのウェブフックエンドポイントに送信します。DevOpsエージェントの調査完了後は、EventBridgeでステータス変化を検知し、後続のLambda関数からDevOpsエージェントのAPIを使用してレポートを取得、SNS経由でメール通知を行います。
なお、エージェントスペースの作成などDevOpsエージェントの基本的な設定は完了している前提とします。詳細は下記ブログを参照ください。
設定
ウェブフックの設定
DevOpsエージェントの設定画面からウェブフックを追加します。
画面を進めると、URLとシークレットキーの生成画面が表示されます。これらは再取得できないため、必ずメモした上で追加をクリックします。
下記のようにウェブフックが追加されます。
DevOpsエージェント連携用Lambdaの設定
ランタイムにPython 3.12を選択し、Lambda関数を作成します。
環境変数
先ほどメモしたURLとシークレットキーを環境変数に設定します。
| キー | 値 |
|---|---|
DEVOPS_AGENT_WEBHOOK_URL |
ウェブフックエンドポイントURL |
DEVOPS_AGENT_WEBHOOK_SECRET |
シークレットキー |
タイムアウト
タイムアウトを1分に設定します。
リソースベースポリシー
CloudWatchアラームからLambdaを直接呼び出すため、以下のリソースベースポリシーを追加します。
| 項目 | 値 |
|---|---|
| Statement ID | AllowCloudWatchAlarmInvoke |
| Principal | lambda.alarms.cloudwatch.amazonaws.com |
| Action | lambda:InvokeFunction |
コード
下記コードを貼り付け、デプロイをクリックします。
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 |
タイムアウト
タイムアウトを30秒に設定します。
IAMロール
検証目的のため、Lambdaの実行ロールに以下のマネージドポリシーを付与します。
AIDevOpsAgentFullAccessAmazonSNSFullAccess
本番環境では最小権限の原則に従い、必要なアクションのみに絞ってください。
コード
下記コードを貼り付け、デプロイをクリックします。
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を使用して調査レポートを取得しています。
下記の箇所で、agentSpaceIdとexecutionIdはEventBridgeから連携されるイベントJSONのメタデータから取得し、recordTypeにinvestigation_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%以下でアラート状態となるアラームを作成します。
ターゲットとしてDevOpsエージェント連携用のLambdaを指定します。
調査の確認
アラーム状態に遷移した後、DevOpsエージェントのWebコンソールからインシデントレスポンスダッシュボードを確認します。先ほどのアラート名で調査が開始されていることが確認できます。
調査が完了すると、下記のようにレポートが表示されます。
メール通知の確認
調査完了後、レポートがメールで送信されます。
# 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と汎用ウェブフックを組み合わせることで連携が可能です。本構成により、アラート発生から原因分析、レポート受信までを自動化でき、運用初動対応の負荷軽減が出来る可能性があります。












