お久しぶりです。shoheiです。

2025年4月の入社から1年、気づけば AWS 認定資格を全冠取得していました(先日 ANS に合格しました)。とはいえ、資格はあくまでスタート地点。実際に手を動かして検証してみると、ドキュメントや資格試験にはない「コツ」が山ほど見つかるなと思うこの頃です。今回はGA直後のAWS DevOps Agentを触ってみました。

はじめに

2026年3月31日、AWS DevOps Agent が一般提供(GA)されました。re:Invent 2025 でプレビューが発表されてから約4か月、東京リージョン(ap-northeast-1)を含む6リージョンで利用可能になっています。

AWS DevOps Agent は、インシデントの自律的な調査、根本原因分析、緩和策の提案を行う AI エージェントです。CloudWatch、CloudTrail といった AWS ネイティブのオブザーバビリティツールに加え、Datadog、Dynatrace、New Relic、Splunk などのサードパーティ製ツール、さらに GitHub、GitLab、ServiceNow、Slack、PagerDuty とも連携できます。

今回は、社内の共有 AWS アカウント上に構築した議事録 RAG 検索システムを対象に、DevOps Agent の設定から自動調査パイプラインの構築・検証までを実施しました。その過程で遭遇したタグフィルタの問題、IAM ポリシー設計の試行錯誤、そして自動調査の実際の精度について、検証結果をまとめます。

 

検証環境

検証で使用するシステムは、RAG ベースのチャットで内容を検索できるアプリケーションです。
今回のメインではないので軽めのご紹介です。後述しますが今回起こすエラー箇所についても記載しています。

アーキテクチャ概要

[Amplify(React SPA)]
    ↓
[API Gateway(HTTP API)] ── Cognito JWT 認証
    ├── POST /upload    → upload_handler Lambda → [S3]
    ├── POST /chat      → chat_handler Lambda  → [Bedrock Knowledge Base + Claude]
    ├── GET  /documents → list_documents Lambda → [DynamoDB]
    └── DELETE /documents/{id} → delete_document Lambda

 [S3]
    ↓ PutObject イベント
[EventBridge] ── InputTransformer で入力変換 ★今回のエラー関連箇所
    ↓
[Step Functions] ── 処理パイプライン
    ├── 音声/動画 → Transcribe → Claude 整形
    ├── テキスト  → テキスト抽出
    ├── KB 同期(Bedrock Knowledge Base)
    └── ステータス更新(DynamoDB)

すべてのリソースは Terraform で管理しており、Application = myapp タグを付与しています。利用リージョンは ap-northeast-1(東京)です。

Agent Space の作成とタグフィルタの課題

Agent Space とは

DevOps Agent の論理的な管理単位は「Agent Space」です。AWS アカウントとの紐付け、サードパーティ連携、IAM ロールの設定などを Agent Space 単位で管理します。

タグフィルタの設定を試みる

共有アカウントには複数のリソースが混在しているため、DevOps Agent の調査対象を自分のアプリケーションに限定したいと考えました。Agent Space 作成画面には 「AWS タグを含める – オプション」 というセクションがあり、ここでタグベースのフィルタリング設定ができるように見えます。

ただ、実際に試してみると…

タグフィルタ自体が機能しない(Agent Space自体のタグになっている!?

正しくタグを設定しても、フィルタリングが期待どおりに動作しませんでした。
※タグが付与されていないリソースも表示されていました。

CLI / API にもタグフィルタに相当するパラメータは存在しません。
create-agent-space--tags は Agent Space 自体の管理タグ用であり、associate-service の AWS アカウント設定(--configuration aws および --configuration sourceAws)にも accountIdaccountTypeassumableRoleArn しか含まれていません

create-agent-space CLI リファレンスassociate-service CLI リファレンス

結論として、共有アカウントでは IAM ポリシーによるスコープ制御を前提にしたほうが現実的です。

ということでIAMポリシーでの制御を行ってみました。

IAM ポリシー設計 — 共有アカウントでのスコープ制御

マネージドポリシーの課題

Agent Space を自動作成すると、AIOpsAssistantPolicy というマネージドポリシーがアタッチされます。このポリシーは EC2、Lambda、S3、DynamoDB、RDS をはじめとする非常に多くの AWS サービスに対して "Resource": "*" で読み取り権限を付与します。

当初の目的通り自分のアプリケーションに限定して調査したいのでなるべく絞ります。

カスタムポリシーの設計方針

そこで、マネージドポリシーをデタッチし、カスタムポリシーで以下の方針を取りました。

方針

  • List / Discover 系:タグ条件なしで全リソースを許可
  • Get / Describe 系:aws:ResourceTag/Application = "myapp" 条件付きで許可
  • CloudWatch / CloudTrail / Step Functions などの監視系:タグ条件なしで許可
  • トポロジ検出(Resource Explorer / CloudFormation):タグ条件なしで許可

ポリシー構造

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "AllowTaggedResources",
            "Effect": "Allow",
            "Action": [
                "lambda:GetFunction",
                "lambda:GetFunctionConfiguration",
                "lambda:List*",
                "dynamodb:DescribeTable",
                "dynamodb:ListTagsOfResource",
                "s3:GetBucketTagging",
                "s3:GetBucketLocation",
                "s3:GetObject",
                "s3:ListBucket",
                "cognito-idp:DescribeUserPool",
                "cognito-idp:ListUserPoolClients",
                "amplify:GetApp",
                "amplify:List*",
                "bedrock:GetKnowledgeBase",
                "bedrock:List*",
                "aoss:BatchGetCollection",
                "aoss:List*"
            ],
            "Resource": "*",
            "Condition": {
                "StringEquals": {
                    "aws:ResourceTag/Application": "myapp"
                }
            }
        },
        {
            "Sid": "AllowCloudWatchCloudTrail",
            "Effect": "Allow",
            "Action": [
                "cloudwatch:Describe*",
                "cloudwatch:GetMetricData",
                "cloudwatch:List*",
                "logs:Describe*",
                "logs:FilterLogEvents",
                "logs:GetLogEvents",
                "logs:StartQuery",
                "logs:GetQueryResults",
                "logs:List*",
                "cloudtrail:LookupEvents",
                "cloudtrail:Describe*",
                "cloudtrail:List*",
                "states:ListExecutions",
                "states:ListStateMachines",
                "states:DescribeExecution",
                "states:DescribeStateMachine",
                "states:GetExecutionHistory",
                "events:ListRules",
                "events:DescribeRule",
                "events:ListTargetsByRule",
                "sns:ListTopics",
                "sns:GetTopicAttributes",
                "xray:GetTraceSummaries",
                "xray:BatchGetTraces"
            ],
            "Resource": "*"
        },
        {
            "Sid": "AllowTopologyDiscovery",
            "Effect": "Allow",
            "Action": [
                "resource-explorer-2:Search",
                "resource-explorer-2:GetDefaultView",
                "resource-explorer-2:List*",
                "tag:GetResources",
                "cloudformation:Describe*",
                "cloudformation:List*"
            ],
            "Resource": "*"
        },
        {
            "Sid": "AllowNetworkContext",
            "Effect": "Allow",
            "Action": [
                "ec2:DescribeVpcs",
                "ec2:DescribeSubnets",
                "ec2:DescribeSecurityGroups",
                "ec2:DescribeNetworkInterfaces"
            ],
            "Resource": "*"
        }
    ]
}

後述する障害検証では、Step Functions の権限(states:DescribeExecution など)を追加する前と後で、調査結果に劇的な差が出ました。権限設計はセキュリティだけの話ではなく、エージェントの調査品質そのものを左右する要素でもあります。

自動調査パイプラインの構築

DevOps Agent は自動調査をサポートしているか?

公式ドキュメント(Autonomous incident response)では、以下の 3 つの方法で調査を開始できると明記されています。

  • ビルトイン連携 — ServiceNow、PagerDuty、Dynatrace などからの自動トリガー
  • Generic Webhook — HTTP POST リクエストによるトリガー
  • 手動 — Web アプリからの手動開始

構築するアーキテクチャ

CloudWatch Alarm のネイティブ連携は存在しないため、今回は Generic Webhook を使って以下のパイプラインを構築しました。

CloudWatch Alarm(ALARM状態)
  ↓
SNS Topic
  ↓
Lambda(HMAC署名付きWebhook送信)
  ↓
DevOps Agent Generic Webhook
  ↓
自動調査開始
  ↓
Slack チャンネルに結果投稿

ステップ1:Slack 連携

DevOps Agent コンソールの Capability Providers → Slack → Register から Slack ワークスペースを認証し、Agent Space の 機能 → コミュニケーション → Slack で通知先チャンネルの Channel ID を設定します。

 

連携が完了すると、調査開始通知、主要な検出結果、根本原因分析、緩和計画が自動的に Slack チャンネルへ投稿されます。

ステップ2:Generic Webhook の生成

Agent Space → Capabilities → Webhook → Configure →「Generate webhook」 で Webhook URL と HMAC Secret を取得します。

HMAC Secret はこの画面でしか確認できません。 そのため、必ず安全な場所に保存しておく必要があります。本番運用では AWS Secrets Manager での管理が無難です。

なお、Webhook の生成はコンソールのみ対応で、CLI / API では list_webhooks のような参照系しか提供されていません。

ステップ3:パイプラインの構築(CLI)

IAM ロール作成

cat > /tmp/webhook-lambda-trust.json << 'EOF'
{
  "Version": "2012-10-17",
  "Statement": [{
    "Effect": "Allow",
    "Principal": {"Service": "lambda.amazonaws.com"},
    "Action": "sts:AssumeRole"
  }]
}
EOF

aws iam create-role \
  --role-name DevOpsAgentWebhookLambdaRole \
  --assume-role-policy-document file:///tmp/webhook-lambda-trust.json

aws iam attach-role-policy \
  --role-name DevOpsAgentWebhookLambdaRole \
  --policy-arn arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole

Lambda 関数

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

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

def lambda_handler(event, context):
    sns_message = json.loads(event["Records"][0]["Sns"]["Message"])

    alarm_name = sns_message.get("AlarmName", "Unknown Alarm")
    reason = sns_message.get("NewStateReason", "")
    region = sns_message.get("Region", "")

    resources = []
    trigger = sns_message.get("Trigger", {})
    for dim in trigger.get("Dimensions", []):
        resources.append(f"{dim.get('name')}:{dim.get('value')}")

    timestamp = 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 = {
        "eventType": "incident",
        "incidentId": incident_id,
        "action": "created",
        "priority": "HIGH",
        "title": f"CloudWatch Alarm: {alarm_name}",
        "description": f"{alarm_name}: {reason}",
        "service": "cloudwatch-alarm",
        "timestamp": timestamp,
        "affectedResources": resources,
        "data": {
            "metadata": {
                "region": region,
                "alarmName": alarm_name
            }
        }
    }

    payload_str = json.dumps(payload)

    sign_input = f"{timestamp}:{payload_str}"
    signature = hmac.new(
        WEBHOOK_SECRET.encode("utf-8"),
        sign_input.encode("utf-8"),
        hashlib.sha256
    ).digest()
    signature_b64 = base64.b64encode(signature).decode("utf-8")

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

    with urllib.request.urlopen(req) as resp:
        status = resp.status
        body = resp.read().decode("utf-8")

    print(json.dumps({
        "incident_id": incident_id,
        "webhook_status": status,
        "webhook_response": body,
        "affected_resources": resources
    }))

    return {"statusCode": status, "body": body}

この Lambda は Python 標準ライブラリのみで実装しているため、外部パッケージのインストールが不要です。

デプロイと SNS 連携

# Lambda デプロイ
cd /tmp && zip -j webhook-lambda.zip lambda_function.py

aws lambda create-function \
  --function-name devops-agent-webhook-trigger \
  --runtime python3.13 \
  --handler lambda_function.lambda_handler \
  --role arn:aws:iam::xxxxxxxxxxxx:role/DevOpsAgentWebhookLambdaRole \
  --zip-file fileb:///tmp/webhook-lambda.zip \
  --timeout 30 \
  --environment "Variables={WEBHOOK_URL=<your-webhook-url>,
WEBHOOK_SECRET=<your-webhook-secret>}" \
  --region ap-northeast-1

# SNS トピック作成
aws sns create-topic \
  --name devops-agent-webhook-trigger \
  --region ap-northeast-1

# Lambda に SNS からの呼び出し許可を付与
aws lambda add-permission \
  --function-name devops-agent-webhook-trigger \
  --statement-id sns-trigger \
  --action lambda:InvokeFunction \
  --principal sns.amazonaws.com \
  --source-arn arn:aws:sns:ap-northeast-1
:xxxxxxxxxxxx:devops-agent-webhook-trigger \
  --region ap-northeast-1

# SNS → Lambda サブスクリプション
aws sns subscribe \
  --topic-arn arn:aws:sns:ap-northeast-1:xxxxxxxxxxxx:devops-agent-webhook-trigger \
  --protocol lambda \
  --notification-endpoint arn:aws:lambda:ap-northeast-1
:xxxxxxxxxxxx:function:devops-agent-webhook-trigger \
  --region ap-northeast-1

既存 CloudWatch アラームに SNS アクションを追加

aws cloudwatch put-metric-alarm \
  --alarm-name "myapp-dev-sfn-execution-failed" \
  --alarm-description "Step Functions パイプライン実行失敗検知" \
  --actions-enabled \
  --alarm-actions "arn:aws:sns:ap-northeast-1
:xxxxxxxxxxxx:devops-agent-webhook-trigger" \
  --metric-name "ExecutionsFailed" \
  --namespace "AWS/States" \
  --statistic "Sum" \
  --dimensions "Name=StateMachineArn,Value=arn:aws:states:ap-northeast-1
:xxxxxxxxxxxx:stateMachine:myapp-dev-processing-pipeline" \
  --period 300 \
  --evaluation-periods 1 \
  --threshold 0.0 \
  --comparison-operator "GreaterThanThreshold" \
  --treat-missing-data "missing" \
  --region ap-northeast-1

障害検証 — Step Functions の意図的エラー

エラーの発生

Step Functions をCLIで実行し、意図的に失敗させます。

冒頭で軽く紹介した通り、本来EventBridgeのInputTransformerを通して変換を行いデータの受け渡しを行いますが今回は生データを渡します。想定しているデータ形式と異なるのでエラーになります。

aws stepfunctions start-execution \
  --state-machine-arn "arn:aws:states:ap-northeast-1:xxxxxxxxxxxx
:stateMachine:myapp-dev-processing-pipeline" \
  --input '{"detail":{"bucket":{"name":"myapp-dev-raw-upload"},
"object":{"key":"test/nonexistent-file.txt"}}}' \
  --region ap-northeast-1

自動調査の流れ

実行から数分後、以下の流れで自動調査が進行しました。

  1. Step Functions 実行が 78ミリ秒で失敗
  2. CloudWatch Alarm が ALARM 状態に遷移(約5分後)
  3. SNS → Lambda → Webhook が発火(約1秒)
  4. Slack に 「Investigation started」 の通知が到着
  5. DevOps Agent が自律的に調査を開始

自動調査タイムラインの確認

エージェントスペースの「オペレータアクセス」をクリック。

 

チャット画面が開いたら「インシデントレスポンス」を開きます。

 

インシデントレスポンスのダッシュボードが表示されました。これで詳しい調査状況を追えます。

 

今回のエラーの調査タイムライン画面です。調査中の場合はリアルタイムで情報取得を行い、エラー原因の特定を行っていきます。

1回目の調査結果(Step Functions 権限なし)

最初のカスタム IAM ポリシーには、Step Functions の権限を含めていませんでした。

この状態でのエージェント出力は、ざっくり以下のようなものでした。

  • 3つの仮説を提示(入力データ検証エラー / IAM 権限不足 / 参照リソースの欠落)
  • いずれも「検証できません」で終了
  • 調査ギャップとして「Step Functions API へのアクセス権限不足」を明記

仮説の方向性自体は正しかったのですが、実行履歴にアクセスできないため、根本原因の特定には至りませんでした。

仮説

調査ギャップ

簡単に言うと「エージェントが調べたかったけど、権限やログが足りなくて調べられなかった箇所」になります。

権限追加

 IAM ポリシーに以下の権限を追加しました。

"states:ListExecutions",
"states:ListStateMachines",
"states:DescribeExecution",
"states:DescribeStateMachine",
"states:GetExecutionHistory"

2回目の調査結果(Step Functions 権限あり)

権限追加後に再調査を依頼したところ、今度は根本原因をかなり正確に特定しました。

エージェントの分析内容は次のとおりです。

完璧ですね

要約すると以下の通り

Step Functions ステートマシンは EventBridge ルールによってトリガーされるように設計されています。このルールには InputTransformer が設定されており、S3 イベントの構造を {"bucket": <bucket>, "s3Key": <key>} の形式に変換します。

しかし、失敗した実行には InputTransformer を経由していない生の S3 イベント形式でデータが提供されました。実行名が単純な UUID であることから、EventBridge 経由ではなく手動または API 経由で直接起動されたと推測されます。

この結果からわかるとおり、エージェントは以下をかなり正確に把握していました。

  • EventBridge InputTransformer の存在と変換ロジック
  • Step Functions のステート定義が期待する入力形式
  • 実際に渡された入力データとの差分
  • 実行名の形式から手動実行であることの推測

エージェントの推測ではありますが実行名の形式から手動実行であるってこともバレました。

Slack への結果投稿

調査完了後、Slack チャンネルには以下の内容が自動投稿されました。

  • 調査開始通知(Investigation started)
  • 検出結果(Findings)
  • 根本原因(Root Cause)の詳細説明
  • 解決済み(resolved)マーク

投稿を見てすぐ状況を理解できるレベルに整理されており、実運用でも十分使える印象でした。

検証結果

  • 自動調査パイプラインの構築:CloudWatch Alarm → SNS → Lambda → Webhook → DevOps Agent → Slack の完全自動フローを構築し、動作確認できた
  • 正確な根本原因分析:EventBridge の InputTransformer の仕様差異まで特定できる精度があった
  • Slack 連携:調査の開始から完了まで、チームに必要な情報が自動投稿される
  • IAM ポリシーによるスコープ制御:タグフィルタの代替として、共有アカウントでのリソース分離が可能だった

    料金について

    DevOps Agent の料金は、エージェントが稼働した時間に対する秒単位課金で、$0.0083/秒です。1回の調査が約5〜7分だとすると、$2.5〜$3.5程度になります。

    初期費用やコミットメントは不要で、AWS Support 利用者には月額クレジットが提供されます。1回ごとの金額はそこまで大きくない一方、アラーム設計によっては回数が積み上がるので、本番導入時はトリガー条件の設計がかなり重要だと感じました。

    まとめ

    AWS DevOps Agent は GA 直後ながら、適切に設定すればかなり実用的な調査精度を発揮します。特に今回の検証で強く感じたのは、「IAM ポリシーの権限範囲が調査品質を決める」という点です。導入を検討するなら、ここは最重要ポイントだと思います。

    タグフィルタの不具合?や CLI 対応の不足など、まだ成熟途上の部分はあります。ただ、それでも CloudWatch Alarm → Webhook → 自動調査 → Slack 通知 という運用フローは十分に構築可能でした。共有アカウントでの利用についても、IAM ポリシーの設計を工夫すれば現実的に運用できます。

    今後は、タグフィルタ対応の改善に期待しつつ、引き続き検証を進めていきたいと思います。