
こんにちは、SRE の松田です。
本記事では、Amazon GuardDuty の脅威検出結果を Bedrock で翻訳・分析し、日本語で具体的な対応手順を含む通知を Slack に送信する方法をご紹介します。
構成

当初は Step Functions → SNS → AWS Chatbot → Slack の構成を検討しましたが、AWS Chatbotの仕様により断念しました。 AWS Chatbot は、SNS 経由で送信されるメッセージに対して、特定の AWS サービスのイベントフォーマットのみをサポートしており、Step Functions から送信されるカスタムメッセージ形式は、AWS Chatbot 側でサポートされていませんでした。
Event received is not supported (see https://docs.aws.amazon.com/chatbot/latest/adminguide/related-services.html)
そのため、Step Functions から EventBridge Connection を経由して通知を送信する構成に変更しました。
前提条件
以下の準備が事前に完了していること
- GuardDuty の有効化
- Amazon Bedrock の Claude 3.5 Sonnet モデルアクセスの有効化
- Slack Webhook URL の発行
参考
- Amazon Bedrock のモデルアクセスの有効化方法: モデルアクセスの有効
- Slack Webhook URL の発行方法: Slack 公式ドキュメント
GuardDuty 検出結果エクスポート間隔の設定変更
GuardDuty の検出結果エクスポート間隔は、デフォルトで 6時間 に設定されています。
セキュリティインシデントの対応を考慮すると、EventBridge への検出結果エクスポート間隔を 15 分に変更することを推奨します。
この設定により、脅威検出から通知までの時間が短縮され、より早い段階での対応が可能になります。
変更手順
- GuardDuty コンソールを開く
- 左メニューの「設定」を押下する
- 「検出結果のエクスポートオプション」の「頻度」を「15 分ごと」を選択する
- 「変更を保存」を押下する
リソース作成
依存関係を考慮して以下の順序で作成します。
1. S3 バケットの作成
Bedrock の RAG(Retrieve and Generate)機能で参照する GuardDuty 公式ドキュメントを格納する S3 バケットを作成します。
Step Functions で Bedrock の RAG 機能を使用する際、参照データソースとして S3 バケットが必要になります。
作成手順
- AWS コンソールで S3 サービスを開く
- 「バケットを作成」を押下
- バケット名:
your-bucket-name - リージョン:
ap-northeast-1
- バケット名:
- GuardDuty 公式ドキュメントのダウンロードおよびアップロード
- GuardDuty Finding Types 公式ドキュメントを PDF 形式でダウンロード
- 作成した S3 バケットに PDF をアップロード

2. EventBridge Connection の作成
Step Functions から Slack Webhook への HTTPS 接続のための EventBridge Connection を作成します。
Step Functions のhttp:invokeリソースを使用する場合、AWS のセキュリティ要件として何らかの認証設定が必須となります。Slack Webhook は URL 自体に認証情報が含まれているため本来は追加の認証は不要ですが、Step Functions の仕様上、EventBridge Connection または認証ヘッダーの設定が必要です。
参考
作成手順
- AWS コンソールで
EventBridgeサービスを開く - 左メニューの「接続」を押下

- 「接続を作成」を押下
- 接続の設定:
- 接続名:
slack-webhook-connection - 説明:
Slack webhook connection for GuardDuty notifications - 認証タイプ: 「API キー」
- API キー名:
xxxxxxxxx - API キー値:
xxxxxxxxx
- 接続名:
Slack の Webhook URL 自体に認証情報が含まれているため、設定した API キーは実際の通信には使用されません。 そのため、実質的には任意の値(ダミー値)でも動作しますが、組織のセキュリティポリシーに応じて適切に設定をしてください。

3. IAM ロールの作成
Step Functions が各 AWS サービスにアクセスするための IAM ロールを作成します。
今回の構成では以下のサービスにアクセスする必要があります。
- Amazon Bedrock: Retrieve and Generate API 呼び出し
- Amazon S3: GuardDuty 公式ドキュメントの読み取り
- Slack Webhook: HTTP 呼び出しによる通知送信
作成手順
- AWS コンソールで
IAMサービスを開く - 左メニューから「ポリシー」を選択
許可ポリシーの作成
「JSON」タブに切り替えて以下をペースト
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": ["bedrock:InvokeModel"], "Resource": "arn:aws:bedrock:ap-northeast-1::foundation-model/anthropic.claude-3-5-sonnet-20240620-v1:0" }, { "Effect": "Allow", "Action": ["bedrock:RetrieveAndGenerate"], "Resource": "*" }, { "Effect": "Allow", "Action": "s3:GetObject", "Resource": "arn:aws:s3:::your-bucket-name/*" }, { "Effect": "Allow", "Action": [ "logs:CreateLogGroup", "logs:CreateLogStream", "logs:PutLogEvents" ], "Resource": "*" }, { "Effect": "Allow", "Action": ["states:InvokeHTTPEndpoint"], "Resource": "*" }, { "Effect": "Allow", "Action": ["events:RetrieveConnectionCredentials"], "Resource": "your-connection-arn" }, { "Effect": "Allow", "Action": [ "secretsmanager:DescribeSecret", "secretsmanager:GetSecretValue" ], "Resource": "*" } ] }名前:
GuardDutyStepFunctionsPolicyyour-bucket-nameとyour-connection-arnを実際の値に置き換えてください
左メニューから「ロール」を選択
- 「ロールを作成」をクリック
- 信頼されたエンティティの設定
- 信頼されたエンティティタイプ:AWS サービス
- サービス:
Step Functions - 「次へ」を押下
- ポリシー名:
GuardDutyStepFunctionsPolicyをアタッチ - ロール名:
GuardDutyStepFunctionsRoleを入力してロールを作成する

4. Step Functions ステートマシンの作成
GuardDuty の分析処理の中心となる Step Functions のステートマシンを作成します。 Lambda 関数の使用も検討しましたが、 ランタイムの管理が不要であることに加え、処理の流れを視覚的に確認できてデバッグもしやすいため、 Step Functions を採用することにしました。
作成手順
- AWS コンソールで
Step Functionsサービスを開く - 「ステートマシンの作成」をクリック
- 設定選択:
- 「空白から作成」を選択
- ステートマシン名:
guardduty-ai-analysis - タイプ: 「標準」を選択
- 実行ロール: 作成済みの
GuardDutyStepFunctionsRoleを選択
- ステートマシン定義
- 「コード」タブに切り替え
- 以下の JSON をペースト
{ "Comment": "Translate GuardDuty threat findings into Japanese with remediation guidance, and deliver notifications to Slack via webhook.", "StartAt": "RetrieveAndGenerate", "States": { "RetrieveAndGenerate": { "Type": "Task", "Parameters": { "Input": { "Text.$": "States.Format('あなたは Amazon GuardDutyの脅威検出結果を分析し、対応方針を示すセキュリティアナリストです。以下の検出結果を基に、明確で簡潔な報告を作成してください。検出タイプ: {}。Amazon GuardDuty の検出結果: {} 報告内容は [概要], [対応方針], [参考] の3つのセクションに分けて記述してください。各セクションの内容は以下の通りです。{}{}[概要] 検出された脅威の説明を簡潔に要約し、影響範囲を明確にしてください。{}{}{}[対応方針] 具体的な対応手順を示してください。{}{}{}[参考] 該当する検知タイプの内容が記載されている Amazon GuardDuty の公式ドキュメントURL を記載してください。', $.detail.type, $.detail.description, '\n', '\n', '\n', '\n', '\n', '\n', '\n', '\n')" }, "RetrieveAndGenerateConfiguration": { "Type": "EXTERNAL_SOURCES", "ExternalSourcesConfiguration": { "ModelArn": "arn:aws:bedrock:ap-northeast-1::foundation-model/anthropic.claude-3-5-sonnet-20240620-v1:0", "Sources": [ { "SourceType": "S3", "S3Location": { "Uri": "s3://your-bucket-name/GuardDuty finding types - Amazon GuardDuty.pdf" } } ] } } }, "Resource": "arn:aws:states:::aws-sdk:bedrockagentruntime:retrieveAndGenerate", "ResultPath": "$.result", "ResultSelector": { "translate.$": "$.Output.Text" }, "Retry": [ { "ErrorEquals": ["States.ALL"], "BackoffRate": 2, "IntervalSeconds": 5, "MaxAttempts": 5 } ], "Next": "ThreatSeverityEvaluation" }, "ThreatSeverityEvaluation": { "Type": "Choice", "Choices": [ { "Variable": "$.detail.severity", "NumericLessThanEquals": 3, "Next": "SeverityLow" }, { "Variable": "$.detail.severity", "NumericLessThanEquals": 6, "Next": "SeverityMedium" }, { "Variable": "$.detail.severity", "NumericGreaterThan": 6, "Next": "SeverityHigh" } ] }, "SeverityLow": { "Type": "Pass", "Result": { "icon": "🔵", "text": "Low" }, "ResultPath": "$.severity", "Next": "SendToSlack" }, "SeverityMedium": { "Type": "Pass", "Result": { "icon": "🟡", "text": "Medium" }, "ResultPath": "$.severity", "Next": "SendToSlack" }, "SeverityHigh": { "Type": "Pass", "Result": { "icon": "🔴", "text": "High" }, "ResultPath": "$.severity", "Next": "SendToSlack" }, "SendToSlack": { "Type": "Task", "Resource": "arn:aws:states:::http:invoke", "Parameters": { "Authentication": { "ConnectionArn": "your-connection-arn" }, "ApiEndpoint": "your-slack-webhook-url", "Method": "POST", "Headers": { "Content-Type": "application/json" }, "RequestBody": { "text.$": "States.Format('*アカウント:* {}\n*リージョン:* {}\n*検出タイプ:* {}\n*重要度 :* {} {}\n\n{}\n\n*[AWS コンソール]*\nhttps://console.aws.amazon.com/guardduty/home?region={}#/findings?search=id%3D{}', $.account, $.region, $.detail.type, $.severity.text, $.severity.icon, $.result.translate, $.region, $.detail.id)" } }, "End": true } } }
- JSON 内の以下を実際の値に置き換えてください
your-bucket-name→ 作成した S3 バケット名your-account-id→ 実際の AWS アカウント IDyour-connection-arn→ EventBridge Connection Arnyour-slack-webhook-url→ 使用する Slack Webhook URL
5. EventBridge ルールの作成
GuardDuty からのセキュリティイベントを検知して、Step Functions ステートマシンにルーティングする EventBridge ルールを作成します。
作成手順
- AWS コンソールで EventBridge サービスを開く
- 左メニューから「ルール」を選択
- 「ルールを作成」をクリック
- 基本設定
- ルール名:
guardduty-security-event-router - 説明:
Route GuardDuty security findings to AI analysis pipeline - イベントバス: default
- ルール名:
- イベントパターンの定義
- イベントソース: AWS サービス
- AWS サービス:
GuardDuty - イベントタイプ:
GuardDuty Finding
- ターゲット(配信先)の設定
- ターゲットタイプ: AWS サービス
- サービス: Step Functions ステートマシン
- ステートマシン: 作成した
guardduty-ai-analysisを選択 - 入力: イベント全体に一致(デフォルト)
- 実行ロールの設定:
- ロール名:
EventBridge-StepFunctions-ExecutionRole - 権限ポリシー:
- ロール名:
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": ["states:StartExecution"], "Resource": "arn:aws:states:ap-northeast-1:your-account-id:stateMachine:guardduty-ai-analysis" } ] }
- JSON 内の以下を実際の値に置き換えてください
your-account-id→ 実際の AWS アカウント ID
6. 動作検証
GuardDuty サンプル検出結果による検証
AWS CloudShell で以下のコマンドを実行して、実際の GuardDuty のサンプル検出結果を生成できます。
aws guardduty create-sample-findings \ --detector-id $(aws guardduty list-detectors --query 'DetectorIds[0]' --output text) \ --finding-types "Backdoor:EC2/DenialOfService.Dns"
GuardDuty の検出結果は 15 分間隔で EventBridge に配信されるため、サンプル生成から実際の通知まで最大 15 分程度の時間がかかります。
Slack に以下のような通知が届けば成功です。

プロンプトの改修などの検証により、通知を早く実施したい場合は、Step Functions で以下の JSON を入力して実行することも可能です。
- Step Functions →
guardduty-ai-analysisを選択 - 「実行の開始」を押下
- 入力: 以下のサンプル GuardDuty イベントをペースト
{ "account": "123456789012", "region": "ap-northeast-1", "detail": { "type": "Backdoor:EC2/DenialOfService.Dns", "description": "EC2 instance i-99999999 is performing DNS requests at a rate that could be indicative of a denial of service attack.", "severity": 8.5, "id": "sample-finding-12345", "resource": { "resourceType": "Instance", "instanceDetails": { "instanceId": "i-99999999" } } } }
GuardDuty の検出結果をただ通知・翻訳するだけでなく、Amazon Bedrock(Claude 3.5 Sonnet)を用いて専門的な分析を行い、AWS 公式ドキュメントに基づいた具体的な対応手順を提示できるようになりました。「何が起きたか」だけでなく、「何をすべきか」まで明確になることで、セキュリティ対応の初動を加速できると考えています。
追記
本記事を公開したところ、参考になるフィードバックをいただきました。 他に良いアプローチがありそうなことが見つかったため、改めて検証して記事にまとめたいと思います。 貴重なご意見ありがとうございました!
モデル選択について
Claude 3.5 Sonnet を使用するのではなく、Nova Lite を活用することで、
コストを抑えつつ、十分な性能を得られる可能性があるとのことでした。
GuardDuty 公式ドキュメントの取り込み方法
GuardDuty の公式ドキュメントを S3 に格納し RAG の検索対象とする構成よりも、 AWS Documentation MCP Server を利用することで、
ドキュメント更新時に手動で S3 を更新する必要がなくなり、常に最新のドキュメントを参照できる状態を保てるというアプローチもあるとのことでした。
最後に、ユニファで一緒に働く仲間を探しています!