GuardDuty Findings を Bedrock で要約して SES でメール通知する AWS サンプルを試してみた

GuardDuty Findings を Bedrock で要約して SES でメール通知する AWS サンプルを試してみた

2026.04.28

はじめに

GuardDuty の検出を Bedrock を使っていい感じに通知したいなーと考えていたんですが、aws-samples にちょうどいいサンプルを見つけました。

https://github.com/aws-samples/analyze-aws-guardduty-findings-with-bedrock

CloudFormation テンプレート 1 本でデプロイできるシンプルな構成だったので、実際に動かして雰囲気を確認してみました。

サンプルの構成

このサンプルは GuardDuty Finding が発生したら EventBridge → SQS → Lambda という流れで処理する構成です。
Lambda の中で Bedrock の Claude にプロンプトを投げて要約させ、整形した HTML メールを SES で送ります。

Architrcture.png

参考:GitHub - aws-samples/analyze-aws-guardduty-findings-with-bedrock · GitHub

リソースとしては以下のものが CloudFormation でまとめて作られます。

リソース 役割
EventBridge Rule GuardDuty Finding を SQS に転送
SQS Queue (Main + DLQ) Findings のバッファリングと失敗時の退避
Lambda Function Bedrock 呼び出しと SES 送信(Python 3.12)
IAM Role Lambda 実行用のロール

DLQ も用意されており、maxReceiveCount: 3 で失敗時の退避先も確保されているのが良いですね。

前提条件

試すにあたって以下の準備が必要です。

  • GuardDuty が有効化されている
  • SES で送信元・宛先メールアドレスが検証済み(サンドボックスの場合は受信側も要検証)
  • 利用リージョンで Bedrock の Claude Sonnet 4.6 モデルアクセスが許可されている

私はサンドボックスのまま試したので、送信元と受信先の両方を SES の検証済み ID に登録しました。

やってみる

デプロイ

リポジトリをクローンして CloudFormation でデプロイします。

test@example.comは任意の値に書き換えてください。同一アドレスでテストするだけなら 1 回の検証で済みます。

> git clone https://github.com/aws-samples/analyze-aws-guardduty-findings-with-bedrock
> cd analyze-aws-guardduty-findings-with-bedrock

> aws cloudformation deploy \
  --template-file guardduty_findings_analyzer_ses.yml \
  --stack-name guardduty-analyzer \
  --capabilities CAPABILITY_IAM \
  --parameter-overrides \
    SenderEmail=test@example.com \
    ReceiverEmail=test@example.com

Successfully created/updated stack が出たらデプロイ完了です。
リソース数も少ないので 1 〜 2 分程度で終わりました。

以下コマンド実行で SES の検証メールが届くので、リンクをクリックして検証を完了させてください。

> aws ses verify-email-identity --email-address test@example.com

サンプルコードの修正

実はこのサンプル、デプロイしたあとに Lambda を動かすとそのままではエラーになります。
サンプル公開時から Bedrock 側の仕様が変わっていることが原因なので、Lambda コードを少し直す必要がありました。

発生したエラー

まず最初に出るのが、旧モデルを on-demand 呼び出しできないというエラーです。

Error processing GuardDuty finding: An error occurred (ValidationException) when
calling the InvokeModel operation: Invocation of model ID
anthropic.claude-3-sonnet-20240229-v1:0 with on-demand throughput isn't supported.
Retry your request with the ID or ARN of an inference profile that contains this model.

サンプルが指定している Claude 3 Sonnet は、現在では推論プロファイル経由でしか呼び出せなくなっています。
せっかくなので新しい Claude Sonnet 4.6 へ乗り換えることにしました。

modelId を差し替えて再デプロイすると、今度は別のエラーが出ます。

Error processing GuardDuty finding: An error occurred (ValidationException) when
calling the InvokeModel operation: `temperature` and `top_p` cannot both be
specified for this model. Please use only one.

新しい Claude モデルでは temperaturetop_p の同時指定はサポートされていません。

修正内容

index.pyguardduty_findings_analyzer_ses.yml 内 ZipFile セクションの bedrock.invoke_model を以下のように差し替えました。

index.py(Before)
logger.info("Invoking Bedrock with Claude 3.5 Sonnet model")
response = bedrock.invoke_model(
    modelId='anthropic.claude-3-sonnet-20240229-v1:0',
    body=json.dumps({
        "anthropic_version": "bedrock-2023-05-31",
        "max_tokens": 1000,
        "messages": [{"role": "user", "content": prompt}],
        "temperature": 0.1,
        "top_p": 1,
        "top_k": 250
    })
)
index.py(After)
logger.info("Invoking Bedrock with Claude Sonnet 4.6 model")
response = bedrock.invoke_model(
    modelId='jp.anthropic.claude-sonnet-4-6',
    body=json.dumps({
        "anthropic_version": "bedrock-2023-05-31",
        "max_tokens": 1000,
        "messages": [{"role": "user", "content": prompt}],
        "temperature": 0.1
    })
)

変更点は以下の通りです。

項目 Before After
modelId anthropic.claude-3-sonnet-20240229-v1:0 jp.anthropic.claude-sonnet-4-6
呼び出し方式 on-demand(直接モデル指定) Cross-Region Inference Profile(JP)
top_p 1 削除
top_k 250 削除

jp. プレフィックスは日本国内(東京・大阪)に限定してルーティングする推論プロファイルです。データレジデンシーを日本に揃えたかったので JP プロファイルを採用しました。グローバルに広げたい場合は global.anthropic.claude-sonnet-4-6 も選択できます。

利用可能なプロファイル ID は以下のコマンドで確認できます。

> aws bedrock list-inference-profiles --region ap-northeast-1 \
  --query 'inferenceProfileSummaries[?contains(inferenceProfileId, `sonnet-4-6`)].[inferenceProfileId,inferenceProfileName]' \
  --output table

修正後にもう一度 aws cloudformation deploy を実行すれば反映されます。

サンプル Finding の生成

GuardDuty には動作確認用にサンプル Findings を生成する機能があるので、これを使って Lambda が動くかを確認します。

> aws guardduty create-sample-findings \
  --detector-id $(aws guardduty list-detectors --query 'DetectorIds[0]' --output text) \
  --finding-types "Recon:EC2/PortProbeUnprotectedPort"

コマンドを実行すると、すぐに EventBridge を経由して SQS にメッセージが入り、Lambda が起動します。

メールの確認

しばらく待つと指定したメールアドレスに通知が届きます。
Severity に応じて赤・橙・黄でヘッダーの色が変わるようになっており、一目で重要度を把握できます。
Screenshot 2026-04-27 午後1.45.06.png
Screenshot 2026-04-27 午後1.45.34.png

Screenshot 2026-04-27 午後1.46.03.png
メール本文には以下の情報が含まれていました。

  • ヘッダー:Severity ラベル(HIGH / MEDIUM / LOW)と数値
  • AWS アカウント、Finding ID、リージョン、リソースタイプ
  • Finding のタイトルと説明
  • インスタンス ID、インスタンスタイプ
  • 接続方向、リモート IP、ポート、プロトコル
  • Bedrock による AI 分析結果
  • 検知時刻(First Seen / Last Seen)

なお Network Details の項目は、今回のサンプル Finding(PortProbe 系)ではすべて N/A 表示になりました。
これは Lambda コードが networkConnectionAction のみを参照しているためです。PortProbe 系 Finding が使う portProbeAction には対応していません。

一方で AI 分析パートはプロンプトに Finding 全体の JSON を渡しているため、ポート情報も含めて Markdown 形式で構造化された分析結果を生成してくれています。

CloudWatch Logs での動作確認

Lambda の実行ログを見ると、Bedrock の呼び出しから SES の送信まで一連の流れがログ出力されています。

Screenshot 2026-04-27 午後1.47.17.png

ちゃんと Invoking Bedrock with Claude Sonnet 4.6 modelSending summary via SES のログが出ていれば期待通り動いている状態です。

気になった点

動かしてみて気づいた点をいくつか挙げておきます。
あくまでサンプルなので、本番投入する場合は手を入れたいところです。

  • Bedrock のモデル指定がそのままだと動かないため修正が必要
  • 通知チャネルが SES のみなので、Slack や Teams を使っている場合は SNS や Chatbot 経由に拡張したくなる
  • プロンプトが英語なので、日本語の運用チームで使うなら日本語での要約に切り替えたい

このあたりは README にも特に注記はないので、そのまま使うのではなく「叩き台」として認識しておくのが良さそうです。

まとめ

GuardDuty Findings を Bedrock で要約して SES でメール通知する AWS サンプルを試してみました。
CloudFormation 1 本でサクッと動かせるので、Bedrock を使った Findings 一次対応の感触をつかむには非常に良い題材だなと感じました。

そのまま本番に使うにはモデルや IAM 周りで手を入れたい部分はありますが、社内検証や PoC のスタート地点としてはおすすめできます。
こうした 「Findings の一次調査が地味に手間だな」と感じることがあれば、とりあえずこのサンプルを動かしてみましょう。
運用改善のヒントが見つかるはずです。

以上、鈴木純がお送りしました。

参考

この記事をシェアする

関連記事