
DevOps Agentの調査結果から承認付きで自動修復してみた
こんにちは。たかやまです。
DevOps Agentは、障害を調べて根本原因を特定し、緩和計画を作ることができます。
調査だけでなく、AIエージェントにより権限を委譲しても問題ない環境では復旧まで任せてみたいというニーズもあると思います。
その期待に合わせて、AWSでは最近エージェントアクション(Directed actions)が追加されました。
ユーザーがDevOps Agentのチャットから指示して承認すると、緩和計画を実行できます。
ただ、こちらはDevOps Agent のチャット上からユーザーが直接指示と承認が必要になるため、どうしても手間を感じる部分ではあります。
こうした課題へのアプローチとして、調査完了のイベントをきっかけに外部の仕組みで修復を実行するサンプルが、AWS Blogで公開されていました。
今回は、このサンプルをまずそのまま動かして流れを確かめ、最後にDevOps Agentが作った緩和計画を実行する形に拡張した版を紹介します。
さきにまとめ
- サンプルはDevOps Agentの調査完了(
Investigation Completed)を起点に調査要約をBedrockに渡し、Lambda Durable Functionsでユーザーの承認を待ってから修復を実行する - Lambdaのタイムアウトの障害で試すと、調査の開始から約7分で承認待ちになり、承認後にTimeoutが3秒から30秒に変わった
- 承認依頼はCloudWatch Logsの1行で届き、LambdaコンソールかCLIで承認する
- 拡張版では、サンプルと同じ
Investigation Completedを起点に同じ実行の緩和計画を読み、変更計画全体を1回の承認で実行する形にした - 拡張版の承認依頼はSlackのボタンでも受けられ、CPUを使い切るプロセスの障害では6手順の変更計画を1回の承認で、約40秒で実行した
- 実行できる操作は許可リスト(
allowedActions)で絞り、同じ一覧からIAMも作る
構成
サンプルの構成はこちらです。
DevOps Agentの調査が完了すると、EventBridgeにInvestigation Completedイベントが送られます。
EventBridgeルールがtrigger関数を呼び、trigger関数が調査要約を取得してDurable Functionを起動し、Lambda Durable Functionsは、処理の途中でユーザーの承認を待つ仕組みです。
Durable Functionは調査要約をBedrockに渡し、承認後はBedrockが選んだツールをTool Lambdaで実行します。
Bedrock自身はAWSを操作しない設計となっています。
サンプルツールはLambdaの設定の取得と更新の2つで、設定を更新するときだけユーザーの承認を待ちます。
やってみる
サンプルのデプロイ
READMEの手順どおり、仮想環境を作ってCDKでデプロイします。
git clone https://github.com/aws-samples/sample-automate-remediation-post-devops-agent-investigation
cd sample-automate-remediation-post-devops-agent-investigation
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
cdk deploy
サンプルで調査から承認
READMEの「Simulate the incident」どおり、タイムアウト3秒でコードの中で10秒sleepする関数devops-agent-timeoutを作り、1回呼んでタイムアウトさせます。
そのあと、DevOps Agentで調査を始めます。

実行の流れは以下のとおりです。
- 調査が完了し、「Investigation Completed」イベントをもとにtrigger関数がDurable Functionを起動
- Bedrockが根本原因(10秒のsleepに対してTimeoutが3秒)を整理し、設定の取得を選択
- Tool Lambdaが設定を読み、Timeout 3秒を返す(承認なし)
- BedrockがTimeoutを30秒に変える更新を選択
- 承認待ちに入る
調査の開始から承認待ちに入るまでは今回は約7分ほどでした。
trigger関数のログでは、「Investigation Completed」イベントを受信し、Durable Functionが起動したことがわかります。

Durable Functionsのイベント履歴では、Bedrockからの回答を確認できます。

Payloadはエンコードされていて読みづらいですが、デコードすると次の内容が返ってきていました。
**現在の設定確認:**
| 項目 | 現在値 |
| ----------- | --------------------- |
| **Timeout** | **3 秒** ← 問題の設定 |
| MemorySize | 128 MB |
| Runtime | python3.14 |
| State | Active |
`time.sleep(10)` に対して Timeout が 3 秒であることを確認。これが原因でタイムアウトが発生しています。
---
## Step 2: Timeout を修正
`time.sleep(10)` が完了するには最低 10 秒必要です。ネットワーク遅延・処理時間などの余裕を加味して **30 秒** に設定します。
この30秒への更新は変更を伴う操作なので、実行の手前で承認待ちになります。
承認待ちに入ると、CloudWatch Logsに承認用のCLIコマンドを含む1行が出力されます。
APPROVAL REQUIRED — callback_id=<callback_id>, tool=lambda_update_function_configuration, params={"FunctionName": "devops-agent-timeout", "Timeout": 30}. Approve: aws lambda send-durable-execution-callback-success --callback-id <callback_id> --cli-binary-format raw-in-base64-out --result '{"approved": true}'

LambdaコンソールのDurable executions に戻ると、承認待ちのCallbackが「開始済み」のまま止まっています。
Callbackの「︙」メニューから「送信成功」を選び、{"approved": true}を送ると承認できます。


自動修復の確認
承認すると、Tool Lambdaがdevops-agent-timeoutの設定を更新し、Bedrockが完了レポートを返して実行がSUCCEEDEDになりました。
devops-agent-timeoutのTimeoutは30秒に変わり、呼び出すと{"statusCode": 200, "body": "Completed"}が返るようになりました。

サンプルをカスタマイズして緩和計画をSlackで承認して実行する
サンプルを動かしてみて、自分の環境で使うには気になる点がありました。
- Bedrockに渡すのは調査要約で、DevOps Agentが作った緩和計画ではない
- Bedrockが返した値が、そのままLambdaの設定更新に渡る
- Tool LambdaのIAMは、アカウント内のすべてのLambda関数が対象になっている
- ツールがLambdaの設定変更だけなので、ほかの障害は直せない
なので今回はサンプルをもとに、緩和計画を受け取って、許可した範囲の操作だけを承認付きで実行できるようにしました。
サンプルからは主に以下の点を変えています。
| 項目 | サンプル | 拡張版 |
|---|---|---|
| Bedrockに渡す本文 | 調査要約 | 同じ実行の緩和計画 |
| ツール | Lambdaの設定の取得と更新 | 任意のAWS APIとEC2上のシェルコマンドを扱う |
| 承認 | 変更系のツール呼び出しごと | 変更計画全体に1回 |
| 承認の経路 | CLIかコンソール | Slackのボタン、CLI、コンソール |
| 実行できる範囲 | IAMは全Lambda関数が対象 | 操作の種類を許可リストで絞る(対象リソースは絞らず、シェルの送り先のインスタンスだけ指定) |
拡張版のコードは、以下のリポジトリに置いています。
緩和計画の取得
拡張版も、サンプルと同じInvestigation Completedイベントを起点にしています。
DevOps Agentから届くイベント(Investigation Completed)
{
"version": "0",
"id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"detail-type": "Investigation Completed",
"source": "aws.aidevops",
"account": "123456789012",
"time": "2026-10-20T12:28:22Z",
"region": "ap-northeast-1",
"resources": ["arn:aws:aidevops:ap-northeast-1:123456789012:agentspace/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"],
"detail": {
"version": "1.0.0",
"metadata": {
"agent_space_id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"task_id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"execution_id": "exe-ops1-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
},
"data": {
"task_type": "INVESTIGATION",
"priority": "HIGH",
"status": "COMPLETED",
"created_at": "2026-10-20T12:19:10.807Z",
"updated_at": "2026-10-20T12:28:22.968Z",
"summary_record_id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
}
}
}
今回の検証では、緩和計画(mitigation_summary_md)は調査と同じ実行の中で記録され、緩和の完了を示すMitigation Completedイベントは届きませんでした。
そのため、調査完了のイベントを受けてから、同じ実行に記録された緩和計画を読みにいく形にしています。
注意点として、Investigation Completedイベントのsummary_record_idは、緩和計画ではなく調査要約のレコードを指します。
data.summary_record_id(string) – The identifier of the summary record containing investigation findings. Included when a summary record was written for the execution, and omitted otherwise, including on a completed investigation that produced no summary. The event does not contain the summary text.日本語翻訳 :
data.summary_record_id(文字列)は、調査の結果を記録した要約レコードのIDです。その実行で要約レコードが書き込まれたときに含まれ、それ以外のとき(要約を作らずに完了した調査を含む)は省略されます。イベントには要約の本文は含まれません。
なのでsummary_record_idは使わず、イベントの実行IDで同じ実行のレコードをListJournalRecordsで取得し、緩和計画(mitigation_summary_md)のうち最も新しいものを使います。
ListJournalRecordsで返る緩和計画のレコード
{
"agentSpaceId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"executionId": "exe-ops1-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"recordId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"recordType": "mitigation_summary_md",
"createdAt": "2026-10-19T22:19:35.224000+09:00",
"content": "# Mitigation Summary\n\n## Action\n…(緩和計画のMarkdown)"
}
Investigation Completedから緩和計画を取得する流れはこちらです。

見つからないときは間隔をあけて取り直し、それでもなければ何も変更せずに終えます。
計画の作成と承認
拡張版では、変更計画全体に対して1回だけ承認を待ちます。
流れは次のとおりです。
- 計画フェーズでは、モデルは読み取り系(Describe・Get・List)のAWS APIだけをその場で実行できる。それ以外の操作は計画の手順に入れさせる
- モデルは、変更とシェルコマンドを実行する順に並べた計画を出す
- 計画の手順を1つずつ許可リストと照合する。拒否した手順は理由をモデルに返して出し直させ、通らなければ承認を依頼せずに終える
- 計画全体に対して1回だけ承認を待つ
- 承認されたら手順を上から順に実行し、失敗した手順で止める
- 実行結果をBedrockに渡し、日本語の報告を書かせる
ツールは以下の3つです。
| ツール | 役割 |
|---|---|
call_aws_api |
boto3で呼べる任意のAWS APIを呼ぶ。読み取りは計画フェーズでその場で、それ以外は承認後に実行する |
run_shell_command |
SSM経由でEC2上のコマンドを実行する。計画の手順としてだけ使え、承認後に実行する |
submit_plan |
計画を提出する |
submit_planはBedrockのstrict tool useにしていて、計画の形をJSON Schemaで固定しています。
submit_plan のスキーマ
手順のparametersはツールの入力をJSON文字列にしたもので、照合の段階でオブジェクトに読み直します。
"submit_plan": {
"lambda_function_name": None,
"strict": True,
# strict のため、すべてのオブジェクトで additionalProperties を false にし、全プロパティを必須にする
"input_schema": {
"type": "object",
"additionalProperties": False,
"required": ["summary", "steps"],
"properties": {
"summary": {"type": "string"},
"steps": {
"type": "array",
"items": {
"type": "object",
"additionalProperties": False,
"required": ["tool", "parameters", "purpose"],
"properties": {
"tool": {"type": "string", "enum": list(PLAN_TOOLS)},
"parameters": {"type": "string"},
"purpose": {"type": "string"},
},
},
},
},
},
},
緩和計画に書かれる操作は障害ごとに違うので、ツールを障害ごとに作るのではなく、AWS APIとシェルのコマンドを広く扱える形にしました。
その代わり、実行できる範囲はcdk.jsonのallowedActionsの一覧で絞ります。
既定の allowedActions
"allowedActions": [
"ec2:Describe*",
"ec2:AuthorizeSecurityGroupIngress",
"ssm:DescribeInstanceInformation",
"ssm:SendCommand",
"ssm:GetCommandInvocation",
"cloudwatch:Describe*",
"cloudwatch:Get*",
"cloudwatch:List*",
"logs:Describe*",
"logs:Get*",
"lambda:GetFunctionConfiguration",
"lambda:ListFunctions",
"lambda:UpdateFunctionConfiguration"
],
"shellTargetInstanceIds": ["<instance-id>"]
CDKはこの一覧からTool LambdaのIAMポリシーを作り、Tool Lambdaのコードも同じ一覧で計画の手順を照合します。
一覧にない操作を含む計画は、承認依頼を出す前に拒否します。
シェルのコマンドを送れるインスタンスも、一覧で指定したものだけです。
また、削除系の操作やIAMなどのアカウント管理系の操作、承認のコールバックを返す操作は、一覧に何を書いても拒否するようにしています。
照合のコード(抜粋)
READ_PREFIXES = ("Describe", "Get", "List")
DESTRUCTIVE_PREFIXES = ("Delete", "Terminate", "Remove", "Revoke", "Deregister", "Purge")
DENIED_SERVICES = ("iam", "organizations", "sts", "account")
# 承認のコールバックをモデル自身に返させない
DENIED_ACTIONS = ("lambda:SendDurableExecutionCallback*",)
def is_always_denied(action):
"""service:Operation が常に拒否する操作に当たるか。"""
prefix, _, operation = action.partition(":")
if prefix.lower() in DENIED_SERVICES:
return True
if any(operation.lower().startswith(p.lower()) for p in DESTRUCTIVE_PREFIXES):
return True
return any(_pattern_matches(p, action) for p in DENIED_ACTIONS)
def is_allowed(action, cfg):
return not is_always_denied(action) and any(_pattern_matches(p, action) for p in cfg.allowed_actions)
def _require_action(action, cfg):
if is_always_denied(action):
raise ToolValidationError(f"{action} は常に拒否される操作です")
if not is_allowed(action, cfg):
raise ToolValidationError(f"{action} は許可リスト(allowedActions)にありません")
広く任せる分、一覧に入っている変更系の操作を止めるのは、ユーザーの承認だけになります。
Slackで承認する
サンプルの承認依頼はCloudWatch Logsの1行だけなので、ログを見に行かないと承認待ちに気づけません。
拡張版では、承認依頼をSlackにも送るようにしました。
Slackで承認を受けるには、次のものを用意します。
- メッセージを送る権限(
chat:write)を付けたSlackアプリ - アプリのボットトークンと署名シークレット
- トークンとシークレットはSecrets Managerに入れる
- 承認依頼を出すチャンネル
- 事前にアプリを招待し、チャンネルIDをCDKに登録する
- 承認できる人を絞る場合は、そのユーザーのSlackユーザーID(指定しなければチャンネルにいる誰でも承認できる)
- Slackアプリの「Interactivity & Shortcuts」のRequest URLに入れる、デプロイで出力されるFunction URL
Slackのメッセージには、計画の概要、手順ごとの目的と実際に送るコマンドやAPIの全文、「承認」「却下」のボタンが入ります。
ボタンを押すと確認ダイアログが出て、「承認する」を押したときに確定します。
ボタンを押したあとの流れは以下のとおりです。
- SlackがFunction URL経由で受信用のLambdaを呼ぶ
- 受信用のLambdaがSlackの署名と時刻を確かめる。通らなければ401を返す
- 承認依頼を出したチャンネルからの操作か、承認できる人かを確かめる
- Durable Functionに承認のコールバックを返す
- 元のメッセージからボタンを外し、判断した人を残す
CloudWatch Logsに出る承認コマンドをCLIで実行する方法と、LambdaコンソールのDurable executionsから送る方法も残しています。
CPU障害で試す
検証用のEC2でreport-batchという名前のプロセスを2本動かし、CPUを100%に張り付かせた状態で、DevOps Agentで調査を始めます。
ここではOS調査用MCPを有効化した状態で調査をしています。
調査が終わると、DevOps Agentの緩和計画が作成されます。
緩和計画の内容

Bedrockは、この緩和計画から6手順の変更計画を組み立てました。
Step 1.1は計画の段階の読み取りで済ませ、条件付きのロールバックは変更計画から外しています。
この変更計画が、承認依頼としてSlackに届きます。
以下がSlackでの承認依頼の内容で、手順の見出しにある「Step1.2」などの番号は、DevOps Agentの緩和計画の手順番号です。

Slackの「承認」を1回押すと、6手順がすべて実行されます。
変更計画の中身と実行結果は以下のとおりです。
手順1〜5はシェルのコマンド、手順6はAWS APIの呼び出しです。
| 手順 | 内容 | 結果 |
|---|---|---|
| 1 | report-batchの稼働状況を確かめる |
2本がそれぞれCPUを99.7%使っていた |
| 2 | report-batchを止める |
SIGTERMだけで2本とも止まった |
| 3 | report-batchを周期起動している仕組みを探す |
見つからなかった |
| 4 | 見つけたsystemd timerを止めて無効にする | timerがなかったので「SKIP」を出して何もせずに終了した |
| 5 | 停止後の状態を確かめる | report-batchは止まり、CPUの上位プロセスはほぼ0%になった |
| 6 | CloudWatchでCPU使用率を確かめる | 停止直前まで100%。停止後のデータポイントはまだ集計されていなかった |
実行が終わると、Slackの元のメッセージからボタンが外れて「承認: @ユーザー名」が残り、スレッドにBedrockからの実行報告が返信されます。

実際にRun Commandの実行履歴とCPU使用率の低下も確認できました。


最後に
今回は、AWS Blogのサンプルをベースに、DevOps Agentの調査完了から承認付きで修復を実行する流れを試してみました。
まずはサンプルのまま動かし、調査要約をもとにBedrockが修復を選び、ユーザーの承認を経て修復されるところまで確認しました。
そのうえで、サンプルだけでは足りなかった部分を補う形でカスタマイズしています。
拡張版では、緩和計画をもとにした修復の計画をSlackに通知し、Slackから承認すると修復が実行されるようになりました。
外部リソースでの作り込みは必要になりますが、DevOps Agentの緩和計画の指示から修復までを自動化することができます。
ぜひ、自動修復まで試してみたい方の参考になれば幸いです。
以上、たかやま(@nyan_kotaroo)でした。












