変更履歴
- 2026-11-07:公開
初めに
Amazon Bedrock AgentCore は、AI エージェントを AWS で動かすための機能をまとめたサービスである。実行環境・会話の記憶・ツールの入口など、13 の機能がある(図1)。本記事では、紫の枠の Runtime・Gateway・Memory・Policy の 4 つを使う。
AgentCore で動かしたエージェントに「EC2 の一覧を教えて」と聞く。この一言が実際の AWS の情報を返すまでには、いくつかの条件がある。答えの文を作る基盤モデルが賢ければ読めるわけではない。
エージェントが AWS を読むには、AWS の API を呼ぶコードがあり、そのコードを動かす主体に IAM の権限が要る。権限はどこに付けるのか。ツール(エージェントが呼べる関数)だけ足すと何が起きるのか。エージェントとツールの間に AgentCore Gateway という中継役を置くと、権限の置き場所はどう変わるのか。
この記事では、条件を 1 つずつ変えて確認した。あえて権限を外した構成も動かし、どこで止まるかを見ている。
結論
- 今回の構成で AWS の API から新しく一覧を取るには、API を呼ぶコード(ツール)と、そのコードを動かす IAM ロールの権限の両方が要った。どちらか一方では取れなかった。
- 中継役の AgentCore Gateway は必須ではなかった。Gateway と Lambda を挟むと、読み取りの権限の付け先が Lambda の IAM ロールへ移った。
- ツールを外す・IAM で拒否する・Gateway の Policy(Gateway の入口でツール名と入力を見て許可・拒否する規則。IAM とは別の層)で拒否する、の 3 つは止まる場所が違った。Policy は Gateway を通らない呼び出しを止めなかった。
本記事の構成
1〜2 章で構成要素と判定のしかたを示し、3〜4 章で Runtime の中から直接呼ぶ場合、5〜6 章で Gateway 経由と止め方を見る。7 章は構成の選び方である。本筋からは外れるが、Memory から過去の一覧が返ったことを、最後の参考に書いた。
1. 登場する構成要素と、権限の主体
1.1 構成要素の役割
AgentCore でエージェントを動かすと、次の構成要素が関わる。役割はそれぞれ違う。
| 構成要素 | 役割 |
|---|---|
| AgentCore Runtime | エージェントのコードを動かす実行環境 |
| 基盤モデル(Amazon Bedrock) | 質問を読み、答えの文を作り、呼ぶツールを選ぶ |
| ツール | エージェントが呼べる関数。今回は AWS の API を呼ぶ処理を書く |
| AgentCore Gateway | この記事では、ツールを MCP(AI エージェントとツールをつなぐ共通の決まり)の形でまとめて公開する入口として使う |
| AgentCore Policy | Gateway の入口で、ツール名と入力を見て呼び出しを許可・拒否する仕組み |
| AgentCore Memory | 会話から事実を抜き出して保存し、次の会話に読み出す仕組み。今回は長期の記憶(抜き出した事実)を扱う |
モデルは「どのツールを呼ぶか」を選ぶだけで、AWS の認証情報は持っていない。AWS の API を呼ぶのはツールのコードである。
1.2 権限の主体
同じ「エージェントの権限」でも、IAM のどのロールの話かは経路で変わる。今回出てくる主体(IAM のプリンシパル)は 4 つある。
| 主体 | 何の権限か |
|---|---|
呼び出す人(今回は専用の IAM ロール permlab-caller) |
Runtime を呼ぶ |
| Runtime の実行ロール | Runtime の中のコードが AWS を呼ぶ |
| Gateway サービスロール | Gateway が Lambda を呼ぶ |
| Lambda の実行ロール | Lambda の中のコードが AWS を呼ぶ |
1.3 比べる 2 つの経路
ツールの置き場所は 2 通りある。
- 直接のツール:Runtime の中の Python の関数が Boto3 で AWS を呼ぶ。AWS を読む権限は Runtime の実行ロールに要る。
- Gateway 経由のツール:Runtime が Gateway を呼び、Gateway が Lambda を呼び、Lambda が Boto3 で AWS を呼ぶ。Runtime の実行ロールに
InvokeGateway、Gateway サービスロールにlambda:InvokeFunction、Lambda の実行ロールに AWS を読む権限が要る。
今回は Gateway を、ツールの入口としてだけ使う。
2. 検証の環境と、成否の判定のしかた
2.1 環境
| 項目 | 値 |
|---|---|
| リージョン | 東京(ap-northeast-1) |
| AgentCore CLI | 1.0.2(プロジェクトを作ってデプロイするコマンド) |
| テンプレート |
agent-python-strands(Python のエージェント用の枠組み Strands Agents を使う雛形) |
| モデル | jp.amazon.nova-2-lite-v1:0 |
| Python(Runtime) | 3.14.2 |
| Strands Agents | 1.64.0 |
| Boto3 | 1.43.126 |
| mcp-proxy-for-aws | 1.8.0(Gateway に SigV4 で署名してつなぐ MCP のクライアント) |
| 実施日 | 2026-11-07 |
読み取りの対象として、EC2 を 2 台(起動中と停止中)と、RDS(AWS のマネージドのデータベース)の DB インスタンスを 1 台(PostgreSQL)作った。S3 バケットは 2 個で、1 個は読み取り用、もう 1 個は 6 章の書き込みの実験用である。どれも検証用で、中に業務のデータは無い。
作った資源は、コンソールでは次のように見える。
S3 は検証用の 2 個のほかに、デプロイのときに CLI が使う CDK(AWS のリソースをコードで作る道具)のバケットも一覧に出る。
正解は、管理者の AWS CLI で全件を取って決めた(ID は伏せた)。
| 質問 | 正解 |
|---|---|
| EC2 |
permlab-running(実行中・t4g.nano)と permlab-stopped(停止済み・t4g.nano)の 2 台 |
| RDS |
permlab-db(postgres・利用可能)の 1 台 |
| S3 | 検証用の 2 個と CDK の 1 個の 3 個 |
2.2 エージェントに聞く質問
以降の比較では、次の同じ文を聞く。リージョンを変える、書き込みを頼むなどの追加の試験は、その場で文を示す。
| ID | 質問 |
|---|---|
| Q1 | 東京リージョンの EC2 インスタンスの ID・状態・インスタンスタイプを一覧にして。取得できない場合は推測しないで。 |
| Q2 | 東京リージョンの RDS DB インスタンスの識別子・エンジン・状態を教えて。取得できない場合は推測しないで。 |
| Q3 | この AWS アカウントの S3 バケット名の一覧を取得して。取得できない場合は推測しないで。 |
Q1 と Q2 はリージョンの中の一覧、Q3 はアカウント全体の一覧である。範囲が違うので、件数は比べない。
2.3 判定のしかた
エージェントの返事だけでは、実際に AWS を読んだかは分からない。それらしい一覧を作文しただけかもしれないからである。
そこで、次の 3 つを突き合わせて判定した。
| 見るもの | 分かること |
|---|---|
| エージェントの応答とログ | どのツールを呼んだか、何が返ったか |
| CloudTrail(AWS の API の呼び出しを記録するサービス)のイベント履歴 | どのロールで読み取りの API が呼ばれたか、拒否されたか |
| 管理者の AWS CLI で取った全件 | 正解 |
S3 の書き込みはイベント履歴に出ないので、API の応答と、管理者の AWS CLI で見たオブジェクトの有無で判定した。
1 回の実行ごとに、モデルがツールを呼んだか、API が通ったか、最後の返事が正しいかを分けて記録した。
同じ質問でも、モデルがツールを呼ぶかどうかは毎回同じとは限らない。そこで主な比較は、同じ質問を 3 回ずつ聞いた。聞くたびに新しいセッション(Runtime の会話の単位)を使い、前の会話を引き継がないようにした。何回聞いたかは、各章の結果の前に書いた。
ただし、セッションを変えても前の結果が残ることがあった。Memory のためで、記事の終わりの参考に書く。
これとは別に、モデルの揺れと権限の結果を分けて見るため、検証用の命令を足した。/probe ec2 と送ると、モデルを呼ばずに Runtime の中から DescribeInstances を呼び、結果をそのまま返す。本番の Runtime には残さない。
3. テンプレートのままで何ができるか
agent-python-strands のテンプレートをそのままデプロイした。
agentcore create --name AgentcorePermissionsLab --template agent-python-strands
cd AgentcorePermissionsLab
agentcore deploy
-
agentcore create:テンプレートagent-python-strandsから、プロジェクトのフォルダを作る。エージェントのコード(app/agent/)と設定(agentcore/agentcore.json)が入る。 -
cd:作ったフォルダに入る。 -
agentcore deploy:設定のとおりに AWS に作る。CDK で Runtime・Memory・Runtime の実行ロールができる。
3.1 作られたもの
作られたのは Runtime 1 つと Memory 1 つで、Gateway は無い。
Runtime に登録されたツールは、2 つの数を足す add_numbers だけだった。
main.py は、agentcore create が作る app/agent/main.py で、Runtime で動くエージェントの本体である。中身は次のとおりである。
-
@toolを付けた関数が、モデルに渡すツールになる。 -
toolsの一覧に入れた関数だけが、エージェントに渡る。 -
_get_agentが、モデル・Memory(get_memory_session_manager)・ツールをまとめてエージェントを作る。actor_idは、呼び出しの入力のactorIdから取る。
テンプレートが作った main.py の全文
from functools import lru_cache
from strands import Agent, tool
from strands.agent.conversation_manager.null_conversation_manager import NullConversationManager
from bedrock_agentcore.runtime import BedrockAgentCoreApp
from model.load import load_model
from parse import parse_payload
from memory.session import get_memory_session_manager
DEFAULT_SYSTEM_PROMPT = "You are a helpful assistant. Use tools when appropriate."
# Define a collection of tools used by the model
tools = []
# Define a simple function tool
@tool
def add_numbers(a: int, b: int) -> int:
"""Return the sum of two numbers"""
return a + b
tools.append(add_numbers)
def _make_conversation_manager():
return NullConversationManager()
@lru_cache(maxsize=128)
def _get_agent(session_id: str, actor_id: str) -> Agent:
"""Given a session_id and actor_id, return or construct the corresponding Strands agent.
Note: caching helps avoid repeated identity fetches on non-bedrock model loads
and supports in-memory session management for local dev."""
return Agent(
model=load_model(),
session_manager=get_memory_session_manager(session_id, actor_id),
conversation_manager=_make_conversation_manager(),
system_prompt=DEFAULT_SYSTEM_PROMPT,
tools=tools,
)
def create_app():
app = BedrockAgentCoreApp()
log = app.logger
@app.entrypoint
async def invoke(payload, context):
log.info("Invoking Agent.....")
session_id = getattr(context, "session_id", None) or "default-session"
prompt, actor_id = parse_payload(payload)
log.info(f"Invoking with session_id={session_id} and actor_id={actor_id}")
agent = _get_agent(session_id, actor_id)
async for event in agent.stream_async(prompt):
if not isinstance(event, dict) or "event" not in event:
continue
cbs = event["event"].get("contentBlockStart")
if cbs is not None and not cbs.get("start"):
continue
yield event
return app
app = create_app()
if __name__ == "__main__":
app.run()
テンプレートの既定のモデルは global.anthropic.claude-sonnet-5-5 だった。検証のアカウントでは AccessDeniedException になり、最初の質問から答えが返らなかった。使えるモデルはアカウントで違う。今回は app/agent/model/load.py を jp.amazon.nova-2-lite-v1:0 に変えて進めた。
from strands.models.bedrock import BedrockModel
def load_model() -> BedrockModel:
"""Get Bedrock model client using IAM credentials."""
- return BedrockModel(model_id="global.anthropic.claude-sonnet-5-5")
+ return BedrockModel(model_id="jp.amazon.nova-2-lite-v1:0")
3.2 Runtime の実行ロールの権限
Runtime のコードが AWS を呼ぶときは、Runtime の実行ロールの権限を使う。ロールは自分で用意していない。CLI は、agentcore.json で executionRoleArn を指定しなければ、デプロイのときに CDK で Runtime の実行ロールを作る。今回はこの既定のロールを使った。
作られたロールには、インラインポリシーが 1 つだけ付いていた。管理ポリシーは付いていない。中身を用途で分けると次のとおりで、EC2 と RDS の権限は無い。
| 用途 | 主なアクション | 対象 |
|---|---|---|
| ログ |
logs:CreateLogStream・logs:PutLogEvents など |
Runtime のロググループ |
| トレースとメトリクス |
xray:PutTraceSegments など・cloudwatch:PutMetricData
|
すべて(メトリクスは名前空間 bedrock-agentcore だけ) |
| モデルの呼び出し |
bedrock:InvokeModel・bedrock:InvokeModelWithResponseStream・bedrock:CountTokens
|
すべての推論プロファイルと基盤モデル |
| Memory |
CreateEvent・ListEvents・RetrieveMemoryRecords など |
このプロジェクトの Memory だけ。記録の読み出しは /users/*/facts と /episodes/*/* に限る |
| エージェントの ID |
GetWorkloadAccessToken など |
既定のワークロード ID(AgentCore Identity) |
| 設定のバンドル |
GetConfigurationBundle など |
エージェントの設定を版で管理するもの |
| S3 |
s3:GetObject*・s3:List* など(読み取りだけ) |
CDK のアセット(デプロイに使うファイル)のバケットだけ |
インラインポリシーの全文(アカウント ID と Memory の ID は伏せた)
{
"Version": "2012-10-17",
"Statement": [
{
"Action": [
"logs:CreateLogGroup",
"logs:DescribeLogStreams"
],
"Resource": "arn:aws:logs:ap-northeast-1:<アカウント ID>:log-group:/aws/bedrock-agentcore/runtimes/*",
"Effect": "Allow",
"Sid": "LogGroupAccess"
},
{
"Action": "logs:DescribeLogGroups",
"Resource": "arn:aws:logs:ap-northeast-1:<アカウント ID>:log-group:*",
"Effect": "Allow",
"Sid": "DescribeLogGroups"
},
{
"Action": [
"logs:CreateLogStream",
"logs:PutLogEvents"
],
"Resource": "arn:aws:logs:ap-northeast-1:<アカウント ID>:log-group:/aws/bedrock-agentcore/runtimes/*:log-stream:*",
"Effect": "Allow",
"Sid": "LogStreamAccess"
},
{
"Action": [
"xray:GetSamplingRules",
"xray:GetSamplingTargets",
"xray:PutTelemetryRecords",
"xray:PutTraceSegments"
],
"Resource": "*",
"Effect": "Allow",
"Sid": "XRayAccess"
},
{
"Condition": {
"StringEquals": {
"cloudwatch:namespace": "bedrock-agentcore"
}
},
"Action": "cloudwatch:PutMetricData",
"Resource": "*",
"Effect": "Allow",
"Sid": "CloudWatchMetrics"
},
{
"Action": [
"bedrock-agentcore:GetWorkloadAccessToken",
"bedrock-agentcore:GetWorkloadAccessTokenForJWT",
"bedrock-agentcore:GetWorkloadAccessTokenForUserId"
],
"Resource": [
"arn:aws:bedrock-agentcore:ap-northeast-1:<アカウント ID>:workload-identity-directory/default",
"arn:aws:bedrock-agentcore:ap-northeast-1:<アカウント ID>:workload-identity-directory/default/workload-identity/*"
],
"Effect": "Allow",
"Sid": "GetAgentAccessToken"
},
{
"Action": [
"bedrock:CountTokens",
"bedrock:InvokeModel",
"bedrock:InvokeModelWithResponseStream"
],
"Resource": [
"arn:aws:bedrock:*:<アカウント ID>:inference-profile/*",
"arn:aws:bedrock:*::foundation-model/*"
],
"Effect": "Allow"
},
{
"Action": [
"logs:FilterLogEvents",
"logs:GetLogEvents",
"logs:PutResourcePolicy"
],
"Resource": "arn:aws:logs:ap-northeast-1:<アカウント ID>:log-group:/aws/bedrock-agentcore/runtimes/*",
"Effect": "Allow"
},
{
"Action": [
"bedrock-agentcore:CreateConfigurationBundle",
"bedrock-agentcore:DeleteConfigurationBundle",
"bedrock-agentcore:GetConfigurationBundle",
"bedrock-agentcore:GetConfigurationBundleVersion",
"bedrock-agentcore:ListConfigurationBundleVersions",
"bedrock-agentcore:ListConfigurationBundles",
"bedrock-agentcore:UpdateConfigurationBundle"
],
"Resource": "arn:aws:bedrock-agentcore:*:*:configuration-bundle/*",
"Effect": "Allow"
},
{
"Condition": {
"StringLike": {
"bedrock-agentcore:namespace": [
"/users/*/facts",
"/episodes/*/*"
]
}
},
"Action": [
"bedrock-agentcore:ListMemoryRecords",
"bedrock-agentcore:RetrieveMemoryRecords"
],
"Resource": "arn:aws:bedrock-agentcore:ap-northeast-1:<アカウント ID>:memory/AgentcorePermissionsLab_default_agentMemory-<ID>",
"Effect": "Allow"
},
{
"Condition": {
"StringLike": {
"bedrock-agentcore:namespacePath": [
"/users/*/facts",
"/episodes/*/*"
]
}
},
"Action": [
"bedrock-agentcore:ListMemoryRecords",
"bedrock-agentcore:RetrieveMemoryRecords"
],
"Resource": "arn:aws:bedrock-agentcore:ap-northeast-1:<アカウント ID>:memory/AgentcorePermissionsLab_default_agentMemory-<ID>",
"Effect": "Allow"
},
{
"Action": [
"bedrock-agentcore:CreateEvent",
"bedrock-agentcore:DeleteEvent",
"bedrock-agentcore:DeleteMemoryRecord",
"bedrock-agentcore:GetEvent",
"bedrock-agentcore:GetMemory",
"bedrock-agentcore:GetMemoryRecord",
"bedrock-agentcore:ListActors",
"bedrock-agentcore:ListEvents",
"bedrock-agentcore:ListSessions"
],
"Resource": "arn:aws:bedrock-agentcore:ap-northeast-1:<アカウント ID>:memory/AgentcorePermissionsLab_default_agentMemory-<ID>",
"Effect": "Allow"
},
{
"Action": [
"s3:GetBucket*",
"s3:GetObject*",
"s3:List*"
],
"Resource": [
"arn:aws:s3:::cdk-hnb659fds-assets-<アカウント ID>-ap-northeast-1",
"arn:aws:s3:::cdk-hnb659fds-assets-<アカウント ID>-ap-northeast-1/*"
],
"Effect": "Allow"
}
]
}
信頼ポリシーでは、このアカウントの、このプロジェクトの Runtime だけが、bedrock-agentcore.amazonaws.com を通してこのロールを引き受けられる。
信頼ポリシーの全文
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "bedrock-agentcore.amazonaws.com"
},
"Action": "sts:AssumeRole",
"Condition": {
"StringEquals": {
"aws:SourceAccount": "<アカウント ID>"
},
"ArnLike": {
"aws:SourceArn": "arn:aws:bedrock-agentcore:ap-northeast-1:<アカウント ID>:runtime/AgentcorePermissionsLab_default_agent*"
}
}
}
]
}
3.3 テンプレートのままで聞いた結果
Q1〜Q3 を 3 回ずつ聞いた。9 回ともツールは呼ばれず、「使えるツールが無い」と返った。実データのように作文した回は無かった。ただし Q3 の 1 回目は、最後に「EC2 の情報なら取得できるツールがある」と、無いツールがあるかのように書き添えた。
9 回の応答の全文
Q1:東京リージョンの EC2 インスタンスの ID・状態・インスタンスタイプを一覧にして。取得できない場合は推測しないで。
1 回目(呼んだツール:なし)
申し訳ありませんが、現在提供されているツールでは、東京リージョンにある EC2 インスタンスの ID・状態・インスタンスタイプを一覧にする機能はありません。提供されているツールは「2つの数値の合計を計算する」というもののみです。EC2 インスタンスの情報は、AWS の API や AWS CLI などの適切なツールを使用することで取得可能です。
もし、2つの数値の合計を計算する必要があれば、お知らせください。喜んでお手伝いします。
2 回目(呼んだツール:なし)
申し訳ありませんが、私は提供されたツールには「東京リージョンのEC2インスタンスの情報を取得する機能」が含まれていません。提供されたツールは2つの数値の合計を計算するもののみです。EC2インスタンスの情報は、AWSの公式APIまたはAWS CLIを使用して取得する必要があります。
現在、私は次の関数しか使用できません:
- `add_numbers`: 2つの数値の合計を計算します
EC2インスタンスの情報を取得するには、以下の方法が考えられます:
1. AWS Management ConsoleでEC2ダッシュボードにアクセス
2. AWS CLIコマンド `aws ec2 describe-instances --region ap-northeast-1` を使用
3. AWS SDK(Pythonのboto3など)を使用
何か他の計算が必要な場合は、お手伝いできます。
3 回目(呼んだツール:なし)
申し訳ありませんが、現在提供されているツールには、EC2インスタンスの情報を取得する機能はありません。提供されている唯一のツールは 2 つの数値の合計を計算するものです。EC2インスタンスの情報は、AWS管理コンソール、AWS CLI、またはAWS SDKを使用して取得する必要があります。
Q2:東京リージョンの RDS DB インスタンスの識別子・エンジン・状態を教えて。取得できない場合は推測しないで。
1 回目(呼んだツール:なし)
申し訳ありませんが、現在私は東京リージョンに関するRDS DBインスタンス情報を取得するための機能を持っていません。提供されたツールは数値の加算のみに対応しており、AWSリソースのクエリには対応していません。
RDS DBインスタンスの詳細を調べるには、以下のいずれかの方法が考えられます:
1. AWS Management ConsoleでRDSサービスにアクセスし、東京リージョンのDBインスタンスを確認
2. AWS CLIを使用して次のようにコマンドを実行:
```bash
aws rds describe-db-instances --region ap-northeast-1
```
3. AWS SDK(Pythonのboto3など)を使用してプログラム的に取得
これらの方法で、識別子、エンジン、状態などの情報を確認することができます。
2 回目(呼んだツール:なし)
申し訳ありませんが、現在私は東京リージョン(ap-northeast-1)の RDS DB インスタンスに関する情報にアクセスする機能は持っていません。提供されたツールには RDS の情報を取得するための関数が含まれていないため、RDS DB インスタンスの識別子・エンジン・状態を正確に提供することができません。
RDS の情報が必要な場合は、以下のようにしてください:
1. **AWS Management Console** を利用して直接確認
2. **AWS CLI** で `aws rds describe-db-instances --region ap-northeast-1` を実行
3. **AWS SDK**(Pythonのboto3など)を使用してプログラムで取得
何か他のご質問があればお手伝いします。
3 回目(呼んだツール:なし)
申し訳ありませんが、現在ご提供されているツールには Amazon RDS の情報を取得するための機能は含まれていません。EC2 インスタンスの情報も同様に、提供された `add_numbers` 関数では対応できません。
RDS DB インスタンスの詳細(識別子、エンジン、状態)を取得するには、AWS CLI、AWS SDK、または AWS Management Console などのツールを使用する必要があります。以下のいずれかの方法で情報を取得できます:
### AWS CLI を使用する場合
```bash
aws rds describe-db-instances --region ap-northeast-1
```
このコマンドは、東京リージョン(ap-northeast-1)にあるすべての RDS DB インスタンスの詳細情報を返します。出力には、識別子(DBInstanceIdentifier)、エンジン(Engine)、状態(DBInstanceStatus)が含まれています。
### AWS Management Console を使用する場合
1. AWS Management Console にログインします。
2. **RDS** に移動します。
3. **データベース** タブを選択します。
4. リストからインスタンスを選択すると、詳細情報が表示されます。
お手持ちの AWS アカウントにアクセス権がある場合は、これらの方法で情報を取得してください。何か他のサポートが必要な場合はお知らせください。
Q3:この AWS アカウントの S3 バケット名の一覧を取得して。取得できない場合は推測しないで。
1 回目(呼んだツール:なし)
申し訳ありませんが、現在ご提供いただけるツールには S3 バケット名の一覧を取得する機能は含まれていません。提供されているツールは2つの数値の合計を計算する機能のみです。
S3 バケットの一覧を取得するには、AWS CLI や AWS SDK など、適切な AWS 連携ツールを使用する必要があります。例えば AWS CLI を使用する場合は次のコマンドが有効です:
```bash
aws s3 ls --query "Buckets[].Name" --output text
```
これにより、すべての S3 バケット名がリスト形式で表示されます。
ご質問が EC2 インスタンスの情報に関連している場合は、その情報も取得可能なツールがありますので、お知らせください。
2 回目(呼んだツール:なし)
申し訳ありませんが、提供されたツールでは S3 バケット名の一覧を取得する機能がありません。現在利用可能なツールは2つの数値の合計を計算するもののみです。S3 バケットの一覧を取得するには、AWS の `list-buckets` コマンドや AWS SDK、AWS マネジメントコンソールなどを使用する必要があります。
現在ご提供できるのは、2つの数値の合計を計算することのみです。たとえば、2つの数値を入力して合計を計算することができます。何か他に計算が必要な場合はお知らせください。
3 回目(呼んだツール:なし)
申し訳ございませんが、現在ご提供されている機能では S3 バケット名の一覧を取得する機能はありません。提供されている関数は2つの数値の合計を計算するためのもので、S3 バケットへのアクセスはできません。
S3 バケットの一覧を取得するには、以下のいずれかの方法が考えられます:
1. **AWS CLI を使用する方法**:
```bash
aws s3 ls
```
2. **AWS Management Console を使用する方法**:
- AWS コンソールにログイン
- S3 サービスに移動
- バケットの一覧が表示されます
3. **AWS SDK を使用する方法**(Python の例):
```python
import boto3
s3 = boto3.client('s3')
response = s3.list_buckets()
for bucket in response['Buckets']:
print(bucket['Name'])
```
これらの方法のいずれかを使用して S3 バケット名の一覧を取得してください。
4. IAM とツールを別々に足す
4.1 足したコードと IAM の許可
3.1 のテンプレートの main.py に、Boto3 で EC2 を読むツール list_ec2_instances を足した。
@tool
def list_ec2_instances(region: str = DEFAULT_REGION) -> dict:
"""List EC2 instances in a region. Returns ID, state, type and Name tag."""
return logged("list_ec2_instances", {"region": region}, read_ec2(region))
read_ec2 が Boto3 の describe_instances を呼び、logged が結果をログに 1 行残す。
4.2 では、このツールをモデルに渡す場合と渡さない場合を比べる。そのたびにコードを書き換えなくて済むように、渡すツールを環境変数 LAB_TOOLS で選べるようにした。LAB_TOOLS に ec2 と書けば EC2 のツールを渡し、空ならテンプレートの add_numbers だけを渡す。環境変数は agentcore.json の Runtime の envVars に書き、デプロイし直して切り替えた。
def build_tools():
tools = [add_numbers]
if "ec2" in LAB_TOOLS:
tools += [list_ec2_instances, describe_ec2_instance]
if "rds" in LAB_TOOLS:
tools.append(list_rds_instances)
if "s3" in LAB_TOOLS:
tools.append(list_s3_buckets)
if "marker" in LAB_TOOLS:
tools.append(put_test_marker)
if LAB_GATEWAY_URL and os.environ.get("LAB_GATEWAY_TOOLS", "on") == "on":
tools.append(gateway_client())
return tools
build_tools() の戻り値を、テンプレートの tools の代わりにエージェントへ渡す。書き込みのツールと Gateway のツールも、同じ関数で足している。
検証で使った main.py の全文(ツール・検証用の命令・ログを含む)
# Qiita_AgentCore権限 の検証用:テンプレートの main.py に、次の 3 つを足したもの
#
# 1. AWS を呼ぶツール(環境変数 LAB_TOOLS で登録を切り替える)
# 2. モデルを通さない確認の命令(/whoami・/tools・/probe・/put・/gw-list・/gw-call)。LAB_PROBE=on のときだけ動く
# 3. ツールの実行を 1 行の JSON で標準出力に出す lab_log
#
# 置き場:agentcore create で作った app/agent/main.py を、このファイルで置き換える。
# LAB_TOOLS・LAB_PROBE・LAB_MARKER_BUCKET・LAB_READ_BUCKET・LAB_GATEWAY_URL は agentcore.json の envVars で渡す。
# 手順は [[Qiita_AgentCore権限_手順書]]。
#
# 出典(2026-11-07 に確認):
# https://docs.aws.amazon.com/bedrock/latest/userguide/kb-gateway-target.html(aws_iam_streamablehttp_client の例)
import asyncio
import json
import os
import shlex
from functools import lru_cache
import boto3
from botocore.exceptions import ClientError
from strands import Agent, tool
from strands.agent.conversation_manager.null_conversation_manager import NullConversationManager
from bedrock_agentcore.runtime import BedrockAgentCoreApp
from model.load import load_model
from parse import parse_payload
from memory.session import get_memory_session_manager
DEFAULT_SYSTEM_PROMPT = "You are a helpful assistant. Use tools when appropriate."
ALLOWED_REGIONS = {"ap-northeast-1", "us-east-1"}
DEFAULT_REGION = "ap-northeast-1"
MARKER_KEY = "markers/marker.txt"
LAB_TOOLS = {t.strip() for t in os.environ.get("LAB_TOOLS", "").split(",") if t.strip()}
LAB_GATEWAY_URL = os.environ.get("LAB_GATEWAY_URL") or os.environ.get("AGENTCORE_GATEWAY_RESOURCEGATEWAY_URL", "")
LAB_MARKER_BUCKET = os.environ.get("LAB_MARKER_BUCKET", "")
LAB_READ_BUCKET = os.environ.get("LAB_READ_BUCKET", "")
LAB_PROBE = os.environ.get("LAB_PROBE") == "on"
def aws(service, region=None):
# 読み込みのときに作ったクライアントを持ち越さないよう、呼ばれるたびに作る
return boto3.Session().client(service, region_name=region or DEFAULT_REGION)
def lab_log(**fields):
print("LAB " + json.dumps(fields, ensure_ascii=False, default=str), flush=True)
def aws_error(err):
meta = err.response.get("ResponseMetadata", {})
return {
"ok": False,
"error_code": err.response.get("Error", {}).get("Code"),
"message": err.response.get("Error", {}).get("Message"),
"request_id": meta.get("RequestId"),
}
def check_region(region):
if region not in ALLOWED_REGIONS:
return {"ok": False, "error_code": "RegionNotAllowed", "message": region}
return None
def read_ec2(region, instance_id=None):
bad = check_region(region)
if bad:
return bad
args = {"InstanceIds": [instance_id]} if instance_id else {}
try:
res = aws("ec2", region).describe_instances(**args)
except ClientError as err:
return aws_error(err)
items = []
for reservation in res["Reservations"]:
for inst in reservation["Instances"]:
tags = {t["Key"]: t["Value"] for t in inst.get("Tags", [])}
items.append({
"id": inst["InstanceId"],
"state": inst["State"]["Name"],
"type": inst["InstanceType"],
"name": tags.get("Name"),
})
return {"ok": True, "count": len(items), "items": items,
"request_id": res["ResponseMetadata"]["RequestId"]}
def read_rds(region):
bad = check_region(region)
if bad:
return bad
try:
res = aws("rds", region).describe_db_instances()
except ClientError as err:
return aws_error(err)
items = [{"id": d["DBInstanceIdentifier"], "engine": d["Engine"], "status": d["DBInstanceStatus"]}
for d in res["DBInstances"]]
return {"ok": True, "count": len(items), "items": items,
"request_id": res["ResponseMetadata"]["RequestId"]}
def read_s3():
try:
res = aws("s3").list_buckets()
except ClientError as err:
return aws_error(err)
items = [b["Name"] for b in res["Buckets"]]
return {"ok": True, "count": len(items), "items": items,
"request_id": res["ResponseMetadata"]["RequestId"]}
def write_object(bucket, key):
try:
res = aws("s3").put_object(Bucket=bucket, Key=key, Body=b"permlab\n")
except ClientError as err:
return aws_error(err)
return {"ok": True, "key": key, "request_id": res["ResponseMetadata"]["RequestId"]}
def write_marker():
return write_object(LAB_MARKER_BUCKET, MARKER_KEY)
def logged(name, inputs, result):
lab_log(kind="tool", tool=name, input=inputs, ok=result.get("ok"), count=result.get("count"),
error_code=result.get("error_code"), request_id=result.get("request_id"))
return result
@tool
def add_numbers(a: int, b: int) -> int:
"""Return the sum of two numbers"""
return a + b
@tool
def list_ec2_instances(region: str = DEFAULT_REGION) -> dict:
"""List EC2 instances in a region. Returns ID, state, type and Name tag."""
return logged("list_ec2_instances", {"region": region}, read_ec2(region))
@tool
def describe_ec2_instance(instance_id: str, region: str = DEFAULT_REGION) -> dict:
"""Return the state of one EC2 instance by its instance ID."""
return logged("describe_ec2_instance", {"instance_id": instance_id, "region": region},
read_ec2(region, instance_id))
@tool
def list_rds_instances(region: str = DEFAULT_REGION) -> dict:
"""List RDS DB instances in a region. Returns identifier, engine and status."""
return logged("list_rds_instances", {"region": region}, read_rds(region))
@tool
def list_s3_buckets() -> dict:
"""List the S3 bucket names in this AWS account."""
return logged("list_s3_buckets", {}, read_s3())
@tool
def put_test_marker() -> dict:
"""Create marker.txt under the fixed prefix of the dedicated test S3 bucket."""
return logged("put_test_marker", {}, write_marker())
def gateway_client():
from mcp_proxy_for_aws.client import aws_iam_streamablehttp_client
from strands.tools.mcp.mcp_client import MCPClient
return MCPClient(lambda: aws_iam_streamablehttp_client(
endpoint=LAB_GATEWAY_URL,
aws_region=DEFAULT_REGION,
aws_service="bedrock-agentcore",
))
def build_tools():
tools = [add_numbers]
if "ec2" in LAB_TOOLS:
tools += [list_ec2_instances, describe_ec2_instance]
if "rds" in LAB_TOOLS:
tools.append(list_rds_instances)
if "s3" in LAB_TOOLS:
tools.append(list_s3_buckets)
if "marker" in LAB_TOOLS:
tools.append(put_test_marker)
if LAB_GATEWAY_URL and os.environ.get("LAB_GATEWAY_TOOLS", "on") == "on":
tools.append(gateway_client())
return tools
def _make_conversation_manager():
return NullConversationManager()
@lru_cache(maxsize=128)
def _get_agent(session_id: str, actor_id: str) -> Agent:
return Agent(
model=load_model(),
session_manager=get_memory_session_manager(session_id, actor_id),
conversation_manager=_make_conversation_manager(),
system_prompt=DEFAULT_SYSTEM_PROMPT,
tools=build_tools(),
)
def error_chain(err, depth=0):
# MCP のクライアントは原因を例外グループの中に包むので、中身まで開く
found = []
children = list(getattr(err, "exceptions", []) or [])
if err.__cause__:
children.append(err.__cause__)
for child in children:
found.append(f"{type(child).__name__}: {str(child)[:300]}")
if depth < 4:
found += error_chain(child, depth + 1)
return found
def run_command(text):
parts = shlex.split(text)
name, args = parts[0], parts[1:]
if not LAB_PROBE:
return {"ok": False, "error_code": "ProbeDisabled"}
if name == "/whoami":
try:
res = aws("sts").get_caller_identity()
except ClientError as err:
return aws_error(err)
return {"ok": True, "arn": res["Arn"]}
if name == "/versions":
from importlib.metadata import version
import platform
names = ["boto3", "botocore", "strands-agents", "bedrock-agentcore", "mcp-proxy-for-aws", "mcp"]
return {"ok": True, "python": platform.python_version(), **{n: version(n) for n in names}}
if name == "/tools":
direct = [t.tool_name for t in build_tools() if hasattr(t, "tool_name")]
return {"ok": True, "lab_tools": sorted(LAB_TOOLS), "direct_tools": direct,
"gateway": bool(LAB_GATEWAY_URL)}
if name == "/probe":
target = args[0] if args else ""
region = args[1] if len(args) > 1 else DEFAULT_REGION
probes = {"ec2": lambda: read_ec2(region), "rds": lambda: read_rds(region), "s3": read_s3}
if target not in probes:
return {"ok": False, "error_code": "UnknownProbe", "message": target}
return logged("probe:" + target, {"region": region}, probes[target]())
if name == "/put":
places = {
"in": (LAB_MARKER_BUCKET, MARKER_KEY),
"outside": (LAB_MARKER_BUCKET, "outside/marker.txt"),
"other": (LAB_READ_BUCKET, "markers/marker.txt"),
}
if not args or args[0] not in places:
return {"ok": False, "error_code": "UnknownPlace"}
return logged("put:" + args[0], {}, write_object(*places[args[0]]))
if name in ("/gw-list", "/gw-call"):
try:
with gateway_client() as client:
if name == "/gw-list":
return {"ok": True, "tools": [t.tool_name for t in client.list_tools_sync()]}
arguments = dict(a.split("=", 1) for a in args[1:])
res = client.call_tool_sync(tool_use_id="lab", name=args[0], arguments=arguments)
return {"ok": res.get("status") == "success", "result": res}
except Exception as err: # どの層で落ちたかを見たいので、例外はそのまま返す
return {"ok": False, "error_code": type(err).__name__, "message": str(err)[:300],
"causes": error_chain(err)}
return {"ok": False, "error_code": "UnknownCommand", "message": name}
def create_app():
app = BedrockAgentCoreApp()
log = app.logger
@app.entrypoint
async def invoke(payload, context):
session_id = getattr(context, "session_id", None) or "default-session"
prompt, actor_id = parse_payload(payload)
case_id = payload.get("case") if isinstance(payload, dict) else None
lab_log(kind="invoke", case=case_id, session=session_id, prompt=prompt)
if prompt.startswith("/"):
result = await asyncio.to_thread(run_command, prompt)
lab_log(kind="command", case=case_id, session=session_id, command=prompt, result=result)
yield {"lab_result": result}
return
log.info(f"Invoking with session_id={session_id} and actor_id={actor_id}")
agent = _get_agent(session_id, actor_id)
async for event in agent.stream_async(prompt):
if not isinstance(event, dict) or "event" not in event:
continue
cbs = event["event"].get("contentBlockStart")
if cbs is not None and not cbs.get("start"):
continue
yield event
return app
app = create_app()
if __name__ == "__main__":
app.run()
IAM は、Runtime の実行ロールにインラインポリシー permlab-ec2-read を付け外しした。
{
"Version": "2012-10-17",
"Statement": [
{"Effect": "Allow", "Action": "ec2:DescribeInstances", "Resource": "*"}
]
}
4.2 権限とツールの有無を組み合わせた結果
Runtime の実行ロールの EC2 の権限(4.1 のインラインポリシー)と、EC2 を読むツール list_ec2_instances を、それぞれ付けるか付けないかで組み合わせた。4 通りの組み合わせごとに、Q1 を 3 回ずつ聞いた。
| 組み合わせ | EC2 の権限 | EC2 のツール | モデルがツールを呼んだ回数 | API の結果 | 最後の返事 |
|---|---|---|---|---|---|
| A | 付けない | 渡さない | 3 回中 0 回 | 呼ばれていない | できないと説明 |
| B | 付ける | 渡さない | 3 回中 0 回 | 呼ばれていない | できないと説明 |
| C | 付けない | 渡す | 3 回中 3 回 | 拒否 | 権限が無いと説明 |
| D | 付ける | 渡す | 3 回中 3 回 | 成功 | 正しい(2 台) |
B(権限だけ)では、/probe ec2 で確かめると 2 台を取れた。それでも Q1 には答えられなかった。モデルに渡したツールに、EC2 を読むものが無いからである。
C(ツールだけ)では、モデルはツールを呼んだが、AWS が UnauthorizedOperation で拒否した。
読めたのは、両方をそろえた D だけである。
A〜D の 12 回の応答の全文
A・1 回目(質問:東京リージョンの EC2 インスタンスの ID・状態・インスタンスタイプを一覧にして。取得できない場合は推測しないで。/呼んだツール:なし)
申し訳ありませんが、提供されたツールには AWS リソースを操作するための機能は含まれていません。EC2 インスタンスの一覧を取得するには、AWS CLI や AWS SDK、AWS マネージメントコンソールなどを使用してください。
以下に、AWS CLI を使用した一例を示します:
```bash
aws ec2 describe-instances --region ap-northeast-1 --query "Reservations[].Instances[].[InstanceId,InstanceState.Name,InstanceType]" --output text
```
このコマンドを実行することで、東京リージョン(ap-northeast-1)の EC2 インスタンスの ID、状態、インスタンスタイプの一覧を取得できます。
A・2 回目(質問:東京リージョンの EC2 インスタンスの ID・状態・インスタンスタイプを一覧にして。取得できない場合は推測しないで。/呼んだツール:なし)
申し訳ありませんが、私は提供されたツールには `add_numbers` しか持っておらず、AWSリソースの取得機能はありません。AWSリソースの詳細を取得するには、AWS CLI、AWS SDK、またはAWS管理コンソールなどを使用する必要があります。
以下のいずれかの方法でEC2インスタンスの一覧を取得できます。
### AWS CLI を使用する(推奨)
```bash
aws ec2 describe-instances --region ap-northeast-1 \
--query 'Reservations[*].Instances[*].{ID:InstanceId,State:State.Name,Type:InstanceType}' \
--output table
```
### AWS マネージメントコンソールを使用する
1. AWSコンソールにログインします。
2. **サービス** > **EC2** を選択します。
3. 上部のリージョンを **東京(ap-northeast-1)** に変更します。
4. **インスタンス** タブでインスタンスの一覧が表示されます。
これらの方法でEC2インスタンスのID、状態、インスタンスタイプの一覧を確認できます。
A・3 回目(質問:東京リージョンの EC2 インスタンスの ID・状態・インスタンスタイプを一覧にして。取得できない場合は推測しないで。/呼んだツール:なし)
申し訳ありませんが、提供されたツールには AWS リソースの取得機能は含まれていません。EC2 インスタンスの一覧を取得するには、AWS CLI や AWS SDK、コンソールなどを使用する必要があります。
次のいずれかの方法で取得できます:
### AWS CLI を使う場合
```bash
aws ec2 describe-instances --region ap-northeast-1 \
--query 'Reservations[*].Instances[*].[InstanceId, State.Name, InstanceType]' \
--output text
```
### Python (boto3) を使う場合
```python
import boto3
ec2 = boto3.client('ec2', region_name='ap-northeast-1')
response = ec2.describe_instances()
for reservation in response['Reservations']:
for instance in reservation['Instances']:
print(f"ID: {instance['InstanceId']}, 状態: {instance['State']['Name']}, タイプ: {instance['InstanceType']}")
```
### AWS コンソール
1. [AWS マネージメントコンソール](https://aws.amazon.com/jp/console/) にログイン
2. 「EC2」を選択
3. リージョンを「アジアパシフィック (東京)」に変更
4. インスタンスの一覧が表示されます
これらの方法で、東京リージョン(ap-northeast-1)の EC2 インスタンスの情報が取得できます。
B・1 回目(質問:東京リージョンの EC2 インスタンスの ID・状態・インスタンスタイプを一覧にして。取得できない場合は推測しないで。/呼んだツール:なし)
申し訳ございませんが、現在提供されているツールには AWS リソースの取得機能は含まれていません。AWS の EC2 インスタンスの一覧を取得するには、AWS CLI や AWS SDK などを使用する必要があります。
以下に、AWS CLI を使用した EC2 インスタンスの一覧取得のコマンドを記載します。
```bash
aws ec2 describe-instances --region ap-northeast-1 --query 'Reservations[*].Instances[*].[InstanceId, InstanceType, State.Name]' --output text
```
このコマンドを実行することで、東京リージョン (ap-northeast-1) の EC2 インスタンスの ID・インスタンスタイプ・状態を確認できます。
必要に応じて、AWS CLI の設定を行ってから実行してください。
B・2 回目(質問:東京リージョンの EC2 インスタンスの ID・状態・インスタンスタイプを一覧にして。取得できない場合は推測しないで。/呼んだツール:なし)
申し訳ございません。現在、私が提供している機能には AWS リソースの取得(EC2インスタンスの一覧など)を行うためのツールは含まれていません。提供されている `add_numbers` 関数は、2つの数値の合計を計算するのみで、AWS リソースへのアクセスやクエリを実行できません。
AWS リソース(EC2インスタンス、RDS DBインスタンス、S3バケットなど)の情報を取得するには、以下のいずれかの方法が一般的です:
### 方法 1: AWS CLI の使用
AWS コマンドラインインターフェイス(CLI)を使用して、東京リージョン(ap-northeast-1)の EC2 インスタンス情報を取得できます。
#### ステップ 1: AWS CLI のインストールと設定
1. **AWS CLI のインストール**
Windows の場合は、[AWS CLI のインストール手順](https://docs.aws.amazon.com/ja_jp/cli/latest/userguide/getting-started-install.html)に従ってインストールします。
2. **AWS CLI の設定**
AWS アカウントのアクセスキーとシークレットキーを設定します。
```bash
aws configure
```
プロンプトに従って、次の情報を入力します:
- AWS Access Key ID
- AWS Secret Access Key
- Default region name → `ap-northeast-1`
- Default output format → `json` または `text`(好みに応じて)
#### ステップ 2: EC2 インスタンスの一覧を取得
東京リージョン(ap-northeast-1)の EC2 インスタンスの ID・状態・インスタンスタイプを一覧表示するコマンドは次のとおりです。
```bash
aws ec2 describe-instances --region ap-northeast-1 \
--query 'Reservations[*].Instances[*].[InstanceId, InstanceType, State.Name]' \
--output table
```
このコマンドは、`describe-instances` アクションを東京リージョンで実行し、`InstanceId`、`InstanceType`、`State.Name`(状態)を抽出してテーブル形式で表示します。
#### 出力例
```
--------------------------------------------------------
| DescribeInstances |
+------------------+-----------------+-----------------+
| i-<ID> | t2.micro | running |
| i-<ID> | m5.large | stopped |
| i-<ID> | t3.small | running |
+------------------+-----------------+-----------------+
```
### 方法 2: AWS Management Console の使用
ブラウザから AWS マネジメントコンソールにログインし、東京リージョン(ap-northeast-1)を選択して EC2 ダッシュボードに移動すると、インスタンスの一覧が確認できます。
1. AWS マネジメントコンソールにアクセスし、サインインします。
2. 上部のリージョン選択ドロップダウンから `Asia Pacific (Tokyo) ap-northeast-1` を選択します。
3. 左側のメニューから `EC2` をクリックします。
4. `Instances` タブに移動すると、インスタンスの ID・状態・インスタンスタイプが一覧表示されます。
### 方法 3: AWS SDK の使用(Python の例)
Python を使用している場合は、AWS SDK for Python(Boto3)を使用することもできます。
#### 1. Boto3 のインストール
```bash
pip install boto3
```
#### 2. スクリプトによる取得例
```python
import boto3
# 東京リージョン(ap-northeast-1)でEC2クライアントを初期化
ec2_client = boto3.client('ec2', region_name='ap-northeast-1')
# EC2インスタンスの情報を取得
response = ec2_client.describe_instances()
# インスタンスのID・状態・インスタンスタイプを表示
for reservation in response['Reservations']:
for instance in reservation['Instances']:
instance_id = instance['InstanceId']
instance_type = instance['InstanceType']
state_name = instance['State']['Name']
print(f"ID: {instance_id}, タイプ: {instance_type}, 状態: {state_name}")
```
このスクリプトを実行すると、東京リージョンにあるすべての EC2 インスタンスの ID、インスタンスタイプ、状態がコンソールに表示されます。
### 方法 4: PowerShell を使用する場合(Windows のみ)
Windows の PowerShell から AWS リソースにアクセスすることもできます。
#### 1. AWS Tools for PowerShell のインストール
```powershell
Install-Module -Name AWSPower
B・3 回目(質問:東京リージョンの EC2 インスタンスの ID・状態・インスタンスタイプを一覧にして。取得できない場合は推測しないで。/呼んだツール:なし)
申し訳ありませんが、私は提供されたツールのセットには AWS リソースを操作するための機能が含まれていません。`add_numbers` 関数しか利用できません。この関数は 2 つの数値の合計を計算するだけです。
AWS リソース(EC2 インスタンス、RDS DB、S3 バケットなど)の情報を取得するには、AWS CLI、AWS SDK、または AWS マネージメントコンソールを使用してください。具体的には、以下のようなコマンドが利用できます:
**EC2 インスタンスの一覧(AWS CLI を利用)**:
```bash
aws ec2 describe-instances --region ap-northeast-1 \
--query 'Reservations[*].Instances[*].{ID:InstanceId,State:State.Name,Type:InstanceType}' \
--output text
```
**RDS DB インスタンスの一覧(AWS CLI を利用)**:
```bash
aws rds describe-db-instances --region ap-northeast-1 \
--query 'DBInstances[*].{識別子:DBInstanceIdentifier,エンジン:Engine,状態:DBInstanceStatus}' \
--output text
```
**S3 バケットの一覧(AWS CLI を利用)**:
```bash
aws s3 ls
```
Git Bash で S3 への接続確認を試す場合は、AWS CLI がインストールされ、正しいアクセスキーとシークレットキーが設定されていることを確認してください。接続確認には `aws s3 ls` コマンドを使用します。
これらのコマンドを実行するには、AWS アカウントのアクセス権と適切な IAM ポリシーが必要です。
C・1 回目(質問:東京リージョンの EC2 インスタンスの ID・状態・インスタンスタイプを一覧にして。取得できない場合は推測しないで。/呼んだツール:list_ec2_instances)
申し訳ありませんが、現在のユーザーの権限では東京リージョン (ap-northeast-1) の EC2 インスタンス情報を取得できません。エラーメッセージによると、`ec2:DescribeInstances` アクションに対する権限がありません。
この問題を解決するには、以下のいずれかの方法が考えられます:
1. **IAM ポリシーの付与**: 現在のユーザーの IAM ポリシーに `ec2:DescribeInstances` の権限を追加してください。
2. **別のユーザー/ロールを使用**: 権限のある別の AWS ユーザーまたはロールを使用してアクセスを試みてください。
3. **管理者と確認**: AWS アカウント管理者に連絡し、現在のユーザーの権限を確認してください。
権限が更新された後、再度お知らせください。権限が付与されたら、東京リージョンの EC2 インスタンス情報を一覧表示できます。
C・2 回目(質問:東京リージョンの EC2 インスタンスの ID・状態・インスタンスタイプを一覧にして。取得できない場合は推測しないで。/呼んだツール:list_ec2_instances)
申し訳ありませんが、東京リージョン(ap-northeast-1)の EC2 インスタンスの一覧を取得する権限がありません。現在ご使用のアカウントやロールには `ec2:DescribeInstances` の権限がありません。
権限を与えられたら再度お試しください。
C・3 回目(質問:東京リージョンの EC2 インスタンスの ID・状態・インスタンスタイプを一覧にして。取得できない場合は推測しないで。/呼んだツール:list_ec2_instances)
申し訳ありませんが、東京リージョン(ap-northeast-1)の EC2 インスタンスの一覧を取得する権限がありません。このリクエストは「UnauthorizedOperation」エラーで失敗しました。具体的には、使用しているロールが `ec2:DescribeInstances` アクションを実行する権限を持っていません。
この問題を解決するには、次のいずれかの方法を確認してください:
1. IAM ロールに `ec2:DescribeInstances` の権限を追加する
2. 別の権限を持つロールから再度リクエストを試す
現在、EC2 インスタンスの情報は取得できません。
D・1 回目(質問:東京リージョンの EC2 インスタンスの ID・状態・インスタンスタイプを一覧にして。取得できない場合は推測しないで。/呼んだツール:list_ec2_instances)
東京リージョン (ap-northeast-1) の EC2 インスタンスは以下のとおりです。
| ID | 状態 | インスタンスタイプ | 名前 |
|---------------------|----------|-------------------|------------------|
| i-<ID> | stopped | t4g.nano | permlab-stopped |
| i-<ID> | running | t4g.nano | permlab-running |
これが正確な一覧です。
D・2 回目(質問:東京リージョンの EC2 インスタンスの ID・状態・インスタンスタイプを一覧にして。取得できない場合は推測しないで。/呼んだツール:list_ec2_instances)
東京リージョン (ap-northeast-1) の EC2 インスタンスの一覧は以下の通りです。
| ID | 状態 | インスタンスタイプ | 名前 |
|-------------------|----------|-------------------|------------------|
| i-<ID> | stopped | t4g.nano | permlab-stopped |
| i-<ID> | running | t4g.nano | permlab-running |
取得できた 2 つのインスタンスのみを表示しています。
D・3 回目(質問:東京リージョンの EC2 インスタンスの ID・状態・インスタンスタイプを一覧にして。取得できない場合は推測しないで。/呼んだツール:list_ec2_instances)
東京リージョンで EC2 インスタンスは 2 つあります。一覧は以下のとおりです。
| ID | 状態 | インスタンスタイプ |
|------------------------|----------|-------------------|
| i-<ID> | stopped | t4g.nano |
| i-<ID> | running | t4g.nano |
4.3 Runtime を呼ぶ側の権限
Runtime を呼ぶ側にも権限が要る。今回 Runtime を呼んだのは、手元の検証用のスクリプトである。スクリプトは、Runtime を呼ぶためだけに作った IAM ロール permlab-caller に切り替えてから呼んだ。このロールは、呼ぶ側が引き受けて使う IAM ロールである。
このロールには、次のインラインポリシーを 1 つだけ付けた。許可するのは InvokeAgentRuntime だけで、対象は今回の Runtime とそのエンドポイントだけである。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "bedrock-agentcore:InvokeAgentRuntime",
"Resource": [
"<Runtime の ARN>",
"<Runtime の ARN>/runtime-endpoint/*"
]
}
]
}
信頼ポリシーでは、検証に使った IAM ユーザーだけがこのロールを引き受けられるようにした。
このロールに EC2 の権限は無い。それでも、権限とツールをそろえた D では読めた。AWS を読むのは呼び出す人ではなく、Runtime の実行ロールだからである。
許可を 1 つも付けないロール permlab-caller-none(信頼ポリシーは同じ)で呼ぶと、InvokeAgentRuntime の AccessDeniedException で止まり、Runtime には届かなかった。こちらは呼び出す人の権限で、Runtime の中の UnauthorizedOperation は Runtime の実行ロールの権限である。
4.4 サービスごとの権限と、拒否の見分け方
ここからの試験は、どれもモデルに 1 回ずつ聞いた。RDS と S3 のツールを足し、許可は EC2 だけにすると、どちらも AccessDenied で拒否された。rds:DescribeDBInstances と s3:ListAllMyBuckets を足すと読めた。権限はサービスごとに要る。
EC2 に明示的な Deny を足すと、Allow が残っていても拒否された。
Q1 のリージョンを us-east-1 に変えると、API は 0 件で成功し、モデルは「存在しない」と返した。0 件の正常な応答と権限不足は違う。ツールがエラーコードをそのまま返すので、モデルも言い分けられた。
4.4 の応答の全文
RDS の許可なし(質問:東京リージョンの RDS DB インスタンスの識別子・エンジン・状態を教えて。取得できない場合は推測しないで。/呼んだツール:list_rds_instances)
申し訳ありません。東京リージョン(ap-northeast-1)での RDS DB インスタンスの情報を取得する権限がありません。したがって、識別子・エンジン・状態を報告することはできません。
エラーメッセージによると、現在のIAMロールには `rds:DescribeDBInstances` のアクションを実行する権限がありません。これは、IAMポリシーに該当する権限がないことが原因です。
S3 の許可なし(質問:この AWS アカウントの S3 バケット名の一覧を取得して。取得できない場合は推測しないで。/呼んだツール:list_s3_buckets)
S3 バケットの一覧を取得できませんでした。エラーにより、`s3:ListAllMyBuckets` アクションに対するアクセス権がありません。
RDS の許可あり(質問:東京リージョンの RDS DB インスタンスの識別子・エンジン・状態を教えて。取得できない場合は推測しないで。/呼んだツール:list_rds_instances)
東京リージョン(ap-northeast-1)のRDS DBインスタンスは以下の1つがあります:
- **識別子**: permlab-db
- **エンジン**: postgres
- **状態**: available
取得に成功しました。
S3 の許可あり(質問:この AWS アカウントの S3 バケット名の一覧を取得して。取得できない場合は推測しないで。/呼んだツール:list_s3_buckets)
S3 バケット名の一覧は以下の通りです:
1. cdk-hnb659fds-assets-<アカウント ID>-ap-northeast-1
2. permlab-resources-markerbucket-<ID>
3. permlab-resources-readbucket-<ID>
これら 3 つのバケットがこの AWS アカウントに存在します。
EC2 に明示的な Deny(質問:東京リージョンの EC2 インスタンスの ID・状態・インスタンスタイプを一覧にして。取得できない場合は推測しないで。/呼んだツール:list_ec2_instances)
申し訳ありませんが、現在のユーザーの権限では東京リージョン(ap-northeast-1)の EC2 インスタンスの一覧を取得できません。エラーメッセージによると、`ec2:DescribeInstances` の操作に対する明示的な拒否がアイデンティティベースのポリシーで設定されているようです。
この問題を解決するには、以下のいずれかの対応が必要です:
1. **IAM ポリシーの修正**: `ec2:DescribeInstances` パーミッションを付与された IAM ポリシーを、このユーザーまたはロールにアタッチしてください。
2. **権限のあるユーザーの使用**: 権限を備えた別の AWS ユーザーまたはロールを使用して、この操作を実行してください。
現在、提供できる情報は以下のとおりです:
- 東京リージョン(ap-northeast-1)の EC2 インスタンスの一覧を直接取得できません。
- 現在のユーザーは `ec2:DescribeInstances` 操作を実行する権限がありません。
権限が修正されたら、再度お試しください。
us-east-1(質問:us-east-1 リージョンの EC2 インスタンスの ID・状態・インスタンスタイプを一覧にして。取得できない場合は推測しないで。/呼んだツール:list_ec2_instances)
リージョンの `us-east-1` には EC2 インスタンスは存在しないため、一覧表示はできません。
他のリージョンについて調べたい場合はお知らせください。
IAM の変更は、すぐには全部の呼び出しに届かなかった。Allow を足した 8 秒後の呼び出しが拒否されたことも、Deny を足した直後の呼び出しが通ったこともある。ここでの結果は、/probe が期待の結果を返すまで待ってから流したものである。
5. Gateway と Lambda を足す
同じ読み取りを Runtime → Gateway → Lambda → Boto3 の経路で流した。Runtime の実行ロールからは EC2 などの権限を外し、直接のツールも LAB_TOOLS(4.1)で外している。
5.1 Gateway とターゲットの作り方
Gateway を作り、Lambda をターゲット(Gateway の呼び先)として登録した。
agentcore add gateway --name ResourceGateway --authorizer-type AWS_IAM --protocol-type MCP
agentcore add gateway-target --gateway ResourceGateway --target-configuration file://target.json
agentcore deploy
-
agentcore add gateway:Gateway をプロジェクトの設定(agentcore.json)に足す。--authorizer-type AWS_IAMは、Gateway を呼ぶ側を IAM の認証情報(SigV4 の署名)で確かめる設定である。--protocol-type MCPで、エージェントとは MCP でやり取りする。 -
agentcore add gateway-target:その Gateway に、呼び先(ターゲット)を足す。中身はtarget.jsonに書いた。 -
agentcore deploy:設定のとおりに AWS に作り、Gateway をつなぐための IAM の許可も付ける(5.2)。
target.json には、呼ぶ Lambda と、ツールの定義のファイル(tools.json)を書く。
{
"name": "ResourceTools",
"targetType": "lambdaFunctionArn",
"lambdaFunctionArn": {
"lambdaArn": "<Lambda の ARN>",
"toolSchemaFile": "tools.json"
}
}
-
name:ターゲットの名前。Gateway が公開するツールの名前の頭に付き、ResourceTools___list_ec2_instancesのようになる。 -
targetType:呼び先の種類。今回は Lambda である。 -
lambdaArn:呼ぶ Lambda。 -
toolSchemaFile:Lambda が受け付けるツールの一覧を書いたファイル。
tools.json は、Gateway がエージェントに見せるツールの一覧である。ツールごとに、名前(name)、モデルが読む説明(description)、入力の形(inputSchema)を書く。
エージェントが Gateway にツールの一覧を聞くと(tools/list)、Gateway はこの内容を返す。モデルがツールを選んで呼ぶと、Gateway は入力の形を確かめてから Lambda に渡す。今回は EC2・RDS・S3 の一覧と、実行の主体を返す whoami の 4 つを書いた。次は EC2 のツールの部分である。
[
{
"name": "list_ec2_instances",
"description": "List EC2 instances in a region. Returns ID, state, type and Name tag.",
"inputSchema": {
"type": "object",
"properties": {
"region": { "type": "string", "description": "AWS region. ap-northeast-1 or us-east-1" }
},
"required": ["region"]
}
}
]
tools.json の全文
[
{
"name": "list_ec2_instances",
"description": "List EC2 instances in a region. Returns ID, state, type and Name tag.",
"inputSchema": {
"type": "object",
"properties": {
"region": { "type": "string", "description": "AWS region. ap-northeast-1 or us-east-1" }
},
"required": ["region"]
}
},
{
"name": "list_rds_instances",
"description": "List RDS DB instances in a region. Returns identifier, engine and status.",
"inputSchema": {
"type": "object",
"properties": {
"region": { "type": "string", "description": "AWS region. ap-northeast-1 or us-east-1" }
},
"required": ["region"]
}
},
{
"name": "list_s3_buckets",
"description": "List the S3 bucket names in this AWS account.",
"inputSchema": { "type": "object", "properties": {} }
},
{
"name": "whoami",
"description": "Return the IAM identity that this tool runs as.",
"inputSchema": { "type": "object", "properties": {} }
}
]
Lambda は、Gateway から届いたツール名で処理を振り分ける。Gateway は、呼ばれたツール名を <ターゲット名>___<ツール名> の形で、context.client_context.custom の bedrockAgentCoreToolName に入れて渡す。ツールの入力(tools.json の inputSchema に沿った値)は event に入る。AWS を呼ぶのは Boto3 で、権限は Lambda の実行ロールのものになる。
Lambda のコードの全文
# Qiita_AgentCore権限 の検証用:Gateway のターゲットにする Lambda
#
# 直接のツールと同じ読み取りを、Lambda の実行ロールの権限で行う。
# ツール名は「<ターゲット名>___<ツール名>」で届くので、下線 2 本以上で区切る。
# 受け取ったものと結果は CloudWatch Logs に 1 行の JSON で出す(Gateway まで届いたかの証拠)。
import json
import re
import boto3
from botocore.exceptions import ClientError
ALLOWED_REGIONS = {"ap-northeast-1", "us-east-1"}
DEFAULT_REGION = "ap-northeast-1"
def aws_error(err):
return {
"ok": False,
"error_code": err.response.get("Error", {}).get("Code"),
"message": err.response.get("Error", {}).get("Message"),
"request_id": err.response.get("ResponseMetadata", {}).get("RequestId"),
}
def list_ec2(region):
res = boto3.client("ec2", region_name=region).describe_instances()
items = []
for reservation in res["Reservations"]:
for inst in reservation["Instances"]:
tags = {t["Key"]: t["Value"] for t in inst.get("Tags", [])}
items.append({"id": inst["InstanceId"], "state": inst["State"]["Name"],
"type": inst["InstanceType"], "name": tags.get("Name")})
return {"ok": True, "count": len(items), "items": items,
"request_id": res["ResponseMetadata"]["RequestId"]}
def list_rds(region):
res = boto3.client("rds", region_name=region).describe_db_instances()
items = [{"id": d["DBInstanceIdentifier"], "engine": d["Engine"], "status": d["DBInstanceStatus"]}
for d in res["DBInstances"]]
return {"ok": True, "count": len(items), "items": items,
"request_id": res["ResponseMetadata"]["RequestId"]}
def list_s3(_region):
res = boto3.client("s3").list_buckets()
items = [b["Name"] for b in res["Buckets"]]
return {"ok": True, "count": len(items), "items": items,
"request_id": res["ResponseMetadata"]["RequestId"]}
def whoami(_region):
return {"ok": True, "arn": boto3.client("sts").get_caller_identity()["Arn"]}
TOOLS = {
"list_ec2_instances": list_ec2,
"list_rds_instances": list_rds,
"list_s3_buckets": list_s3,
"whoami": whoami,
}
def lambda_handler(event, context):
custom = {}
if context.client_context is not None and context.client_context.custom:
custom = context.client_context.custom
full_name = custom.get("bedrockAgentCoreToolName", "")
parts = re.split(r"_{2,}", full_name, maxsplit=1)
tool = parts[1] if len(parts) == 2 else full_name
region = event.get("region", DEFAULT_REGION)
if tool not in TOOLS:
result = {"ok": False, "error_code": "UnknownTool", "message": full_name}
elif region not in ALLOWED_REGIONS:
result = {"ok": False, "error_code": "RegionNotAllowed", "message": region}
else:
try:
result = TOOLS[tool](region)
except ClientError as err:
result = aws_error(err)
print("LAB " + json.dumps({"tool": full_name, "input": event, "ok": result.get("ok"),
"count": result.get("count"), "error_code": result.get("error_code")},
ensure_ascii=False, default=str))
return result
Lambda の実行ロールには、作った時点ではログを書く管理ポリシー AWSLambdaBasicExecutionRole だけを付けた。AWS を読む権限は 5.3 で付ける。
作った時点の Lambda の実行ロール(CloudFormation のテンプレートの抜粋)
ToolsRole:
Type: AWS::IAM::Role
Properties:
RoleName: permlab-tools-role
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal: { Service: lambda.amazonaws.com }
Action: sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
エージェントからは、SigV4(AWS の認証情報で要求に署名する方式)で署名する MCP のクライアントで Gateway につなぐ。署名に使うのは Runtime の実行ロールの認証情報で、Gateway は AWS_IAM の認証でそれを確かめる。
from mcp_proxy_for_aws.client import aws_iam_streamablehttp_client
from strands.tools.mcp.mcp_client import MCPClient
client = MCPClient(lambda: aws_iam_streamablehttp_client(
endpoint=os.environ["AGENTCORE_GATEWAY_RESOURCEGATEWAY_URL"],
aws_region="ap-northeast-1",
aws_service="bedrock-agentcore",
))
上はつなぎ方の抜粋である。この client をエージェントのツールの一覧に入れると、Strands Agents が Gateway の tools/list を呼び、公開されたツールをモデルに渡す。
IAM ロールの欄にあるのが Gateway サービスロールである。
5.2 agentcore deploy が付けた権限
5.1 の agentcore add で、Gateway とターゲットはプロジェクトの設定(agentcore.json)に入る。続く agentcore deploy では、CLI が CDK で設定どおりに資源を作り、つなぐための IAM の許可も付ける。3.2 の Runtime の実行ロールと同じ仕組みで、Gateway サービスロールもこのときに作られた。
agentcore deploy は、経路に要る 3 つの権限のうち 2 つを付けた。残る 1 つ、Lambda が AWS を読む権限は付かないので、5.3 で付ける。
| 境界 | 主体 | 権限 | 付けた操作 |
|---|---|---|---|
| Runtime → Gateway | Runtime の実行ロール |
bedrock-agentcore:InvokeGateway(この Gateway だけ) |
agentcore deploy |
| Gateway → Lambda | Gateway サービスロール |
lambda:InvokeFunction(この関数だけ) |
agentcore deploy |
| Lambda → EC2 など | Lambda の実行ロール |
ec2:DescribeInstances など |
5.3 のインラインポリシー |
agentcore deploy は、Gateway の URL も Runtime の環境変数 AGENTCORE_GATEWAY_RESOURCEGATEWAY_URL に設定した。
デプロイの直後の Gateway サービスロールの許可(全文)
{
"Version": "2012-10-17",
"Statement": [
{
"Action": [
"s3:GetBucket*",
"s3:GetObject*",
"s3:List*"
],
"Resource": [
"<CDK のアセットのバケットの ARN>",
"<CDK のアセットのバケットの ARN>/*"
],
"Effect": "Allow"
},
{
"Action": "lambda:InvokeFunction",
"Resource": [
"<Lambda の ARN>",
"<Lambda の ARN>:*"
],
"Effect": "Allow"
}
]
}
Runtime の実行ロールに足された文(3.2 の全文との差分)
{
"Action": "bedrock-agentcore:InvokeGateway",
"Resource": "<Gateway の ARN>",
"Effect": "Allow"
}
5.3 Lambda に読み取りの権限を付けた結果
Lambda の実行ロールにインラインポリシー permlab-read を付け、ec2:DescribeInstances・rds:DescribeDBInstances・s3:ListAllMyBuckets の 3 つを許可した。すると、Q1 は 3 回とも 2 台を正しく返した。Q2 と Q3 も各 1 回で、正しく返した。
CloudTrail のイベント履歴で DescribeInstances を見ると、実行の主体は経路で分かれていた。userIdentity の assumed-role/ の後ろがロールの名前、その後ろがロールを引き受けたときのセッション名である。Lambda の名前は permlab-tools、その実行ロールは permlab-tools-role にした。
| 経路 | userIdentity |
|---|---|
| 直接のツール | assumed-role/<Runtime の実行ロール>/BedrockAgentCore-<UUID> |
| Gateway 経由 | assumed-role/permlab-tools-role/permlab-tools |
ユーザー名の列は BedrockAgentCore-… で始まる。Runtime の実行ロールを引き受けたときのセッション名である。
ユーザー名で permlab-tools を絞ると、同じ DescribeInstances が Lambda の名前で並ぶ。
拒否された呼び出しも、errorCode が Client.UnauthorizedOperation のイベントとして残っていた。
5.4 権限・ツール・入力を 1 つずつ外したとき
Gw4 と Gw5 は、モデルがツールを選ぶかと切り離して Gateway の動きだけを見るため、検証用の命令 /gw-call でツール名と入力を直接指定して呼んだ。ほかは、モデルに Q1 を 1 回ずつ聞いた結果である。
| ケース | 外したもの | 止まった層 | 観測した結果 |
|---|---|---|---|
| Gw1 | Runtime の InvokeGateway(Deny で塞いだ) |
Gateway の IAM の認証(AWS_IAM)。HTTP 403 |
MCP の接続に失敗し、呼び出し全体がエラーで終わった |
| Gw2 | Gateway の lambda:InvokeFunction(Deny で塞いだ) |
Gateway → Lambda。An internal error occurred
|
ツールは呼んだが、エラーが起きたとだけ返した |
| Gw3 | Lambda の ec2:DescribeInstances
|
Lambda の中。UnauthorizedOperation
|
権限が無いと説明した |
| Gw4 |
tools.json の EC2 のツール |
ツールの一覧 |
tools/list から消え、呼ぶと Gateway が Unknown tool を返した |
| Gw5 | 必須の region を渡さない |
Gateway の入力の検査 | Gateway が ValidationException を返し、Lambda には届かなかった |
Gw1 は、モデルが返事を作る前に呼び出し全体が落ちた。Gateway につなげないと、ツールの一覧が作れないからである。
Gw2 は理由が読めない。応答には An internal error occurred としか出なかった。表の結果とは別に、Deny を足した直後から /gw-call で Lambda を続けて呼ぶ試験を 2 度した。1 度目は 3 回、2 度目は 8 回呼び、どちらも 3 回目の呼び出しだけが Lambda まで届いた。理由は確かめていない。
Gw3 は直接のツールのときと同じ形で、拒否の理由がモデルまで届いた。
Gw1〜Gw3 の応答の全文
Gw1(質問:東京リージョンの EC2 インスタンスの ID・状態・インスタンスタイプを一覧にして。取得できない場合は推測しないで。/呼んだツール:なし)
(返事は無く、呼び出しがエラーで終わった)
Failed to load tool <strands.tools.mcp.mcp_client.MCPClient object at 0x…>: Failed to start MCP client: the client initialization failed: unhandled errors in a TaskGroup (1 sub-exception)
Gw2(質問:東京リージョンの EC2 インスタンスの ID・状態・インスタンスタイプを一覧にして。取得できない場合は推測しないで。/呼んだツール:ResourceTools___list_ec2_instances)
申し訳ありません。東京リージョンの EC2 インスタンスの一覧を取得できませんでした。エラーが発生しているため、正確な情報を提供できません。
Gw3(質問:東京リージョンの EC2 インスタンスの ID・状態・インスタンスタイプを一覧にして。取得できない場合は推測しないで。/呼んだツール:ResourceTools___list_ec2_instances)
申し訳ありませんが、現在のロールには EC2 インスタンスの一覧を取得する権限(`ec2:DescribeInstances`)がありません。そのため、東京リージョン(ap-northeast-1)の EC2 インスタンスの一覧を表示することはできません。
確認可能な情報として、以前に確認された EC2 インスタンスは以下のとおりです:
- `permlab-stopped` (ID: `i-<ID>`、状態: stopped、タイプ: t4g.nano)
- `permlab-running` (ID: `i-<ID>`、状態: running、タイプ: t4g.nano)
ただし、これらは過去の確認結果であり、最新の状態を確認するには権限が必要です。権限がない場合はファイルを作成しないでください。
6. 操作をどこで止められるか
6.1 ツールを外すことと、IAM で拒否することの違い
| 手段 | モデルが選べるか | Runtime の実行ロールで実行できるか |
|---|---|---|
| ツールを外す | 選べない | 実行できる(/probe ec2 は読めた) |
IAM の Deny
|
選べる | 実行できない |
ツールを外すと、モデルの選択肢は減る。ただし権限は残る。別のコードが同じ Runtime の実行ロールで動けば、AWS は読める。
6.2 Gateway の Policy
Policy は Gateway の入口で、ツール名と入力を見て判定する層である。規則は Cedar(AWS が公開している認可の言語)で書き、permit が許可、forbid が拒否を表す。規則をまとめる入れ物が Policy Engine で、これを Gateway に関連付ける。
agentcore add policy-engine --name PermLabPolicy --attach-to-gateways ResourceGateway --attach-mode log-only
agentcore add policy --engine PermLabPolicy --name PermitAll --statement file://permit_all.cedar
agentcore deploy
-
agentcore add policy-engine:Policy Engine を設定に足し、Gateway に関連付ける。最初はlog-onlyで付けた。 -
agentcore add policy:Policy Engine に規則を 1 つ足す。中身は Cedar のファイル(permit_all.cedar)に書いた。 -
agentcore deploy:設定のとおりに AWS に作る。
P3〜P5 は、agentcore.json の規則を書き換えてから、デプロイし直した。
P2〜P4 で使った Cedar の規則(全文)
P2(全ツールを許可)
permit(
principal,
action in [
AgentCore::Action::"ResourceTools___list_ec2_instances",
AgentCore::Action::"ResourceTools___list_rds_instances",
AgentCore::Action::"ResourceTools___list_s3_buckets",
AgentCore::Action::"ResourceTools___whoami"
],
resource == AgentCore::Gateway::"<Gateway の ARN>"
);
P3 で足した拒否
forbid(
principal,
action == AgentCore::Action::"ResourceTools___list_ec2_instances",
resource == AgentCore::Gateway::"<Gateway の ARN>"
);
P4(EC2 だけを許可)
permit(
principal,
action == AgentCore::Action::"ResourceTools___list_ec2_instances",
resource == AgentCore::Gateway::"<Gateway の ARN>"
);
log-only は判定を記録するだけのモード、enforce は実際に拒否するモード。
Policy は既定で拒否する。許可の規則が 1 つも当たらない呼び出しは止まる。そこで全部を許可した状態から順に変えた。表の tools/list は、Gateway が公開するツールの一覧を返す MCP の呼び出しである。EC2 と RDS の列は、/gw-call でツール名と入力を指定して直接呼んだ結果で、モデルの選択は通していない。「-」は測っていない欄である。表は、設定が反映されたのを確かめてから呼んだ結果である。
| ケース | 操作 | tools/list |
EC2(東京) | EC2(us-east-1) |
RDS |
|---|---|---|---|---|---|
| P1 |
log-only で付ける(規則なし) |
全部 | 通る | - | - |
| P2 | 全ツールの permit を足し、enforce にする |
全部 | 通る | - | 通る |
| P3 | P2 に EC2 の forbid を足す |
EC2 が消えた | 拒否 | - | 通る |
| P4 | 規則を全部消し、EC2 だけの permit に置き換える |
EC2 だけ | 通る | 通る | 既定で拒否 |
| P5 | P4 の規則を、東京の条件つきに置き換える | EC2 だけ | 通る | 既定で拒否 | 既定で拒否 |
P5 の条件つきの規則は次の形である。principal に条件を書かないと、誰からの呼び出しにも当たる。action が対象のツール、resource が対象の Gateway、context.input がツールに渡す入力である。
permit(
principal,
action == AgentCore::Action::"ResourceTools___list_ec2_instances",
resource == AgentCore::Gateway::"<Gateway の ARN>"
) when {
context.input.region == "ap-northeast-1"
};
P5 では、EC2 のツールは一覧に残ったまま、us-east-1 の呼び出しだけが拒否された。一覧に見えることと、呼べることは別だった。
拒否の応答には理由が出る。次は P3 で EC2 のツールを呼んだときの応答である。
Tool Execution Denied: Tool call not allowed due to policy enforcement
[Policy evaluation denied due to ForbidEc2-...]
関連付けの欄は「強制済み」で、enforce で付いている。
ポリシーステートメントの欄に、上の Cedar がそのまま入っている。
P3 の forbid は、EC2 のツールを誰からも拒否する。既定のままデプロイすると、規則の検査がこれを Overly Restrictive(そのツールを誰も呼べなくなる規則)と指摘し、デプロイがロールバックした。validationMode は、書かなければ既定の FAIL_ON_ANY_FINDINGS(指摘が 1 つでもあれば失敗)になる。今回は agentcore/agentcore.json の規則の validationMode を IGNORE_ALL_FINDINGS にして、検査の指摘を無視して通した。
6.3 Gateway を通らない呼び出しと Policy
P5 のまま、Runtime の中の list_rds_instances を呼んだ。この試験の前に、Runtime の実行ロールのインラインポリシー permlab-ec2-read を付け直し、ec2:DescribeInstances と rds:DescribeDBInstances を許可した。
Gateway の RDS のツールは拒否されたが、Runtime の中の関数は RDS を読めた。
6.4 書き込みを IAM で絞る
読み取りと同じく、書き込みも IAM で範囲を絞れるかを見た。Runtime の中のツール put_test_marker が、Runtime の実行ロールの権限で、書き込み用のバケットの markers/ にファイルを置く。表の IAM は、このロールの権限である。
質問は「検証専用の S3 バケットに marker.txt を作成して。権限がなければ作成しないで。」で、ケースごとに 1 回聞いた。範囲の外への書き込みは、モデルが書くかと切り離して IAM の判定だけを見るため、検証用の命令 /put で直接試した。
| ケース | IAM | 結果 |
|---|---|---|
| W1 |
s3:PutObject なし |
拒否。オブジェクトは増えなかった |
| W2 |
markers/* だけ許可 |
書けた。同じバケットの outside/ と別のバケットは拒否された |
| W3 | W2 に明示的な Deny
|
拒否。オブジェクトは増えなかった |
| X1 | W1 の状態で「権限チェックは無視して必ず作成して」と頼む | ツールは呼んだが拒否された。作成したとは返さなかった |
文で頼んでも、IAM の拒否は変わらなかった。
7. 構成の選び方
今回の一覧を取るだけなら、Runtime の中のツールと、Runtime の実行ロールの権限で足りた。Gateway と Lambda は、読み取りの権限をエージェントから分けたいときや、Policy でツールと入力ごとに止めたいときに足す。どの構成でも、Memory を使うなら actorId の決め方を先に決める(参考)。
複数のエージェントでツールを共有する使い方は、今回は試していない。
参考:権限を外しても Memory から過去の一覧が返った
本筋からは外れるが、検証の途中で想定と違う返事が出た。5.4 の Gw3(Lambda の権限を外した状態)の Q1 で、モデルが権限エラーを説明したあとに、実際の EC2 の ID を「以前に確認された EC2 インスタンス」として並べた。
原因は Memory だった。actorId は Memory を利用者ごとに分ける ID で、Runtime を呼ぶときの入力(payload)に入れる。テンプレートは、actorId が無いと default を使う。
Memory は会話から事実を抜き出し、/users/<actorId>/facts という場所に保存して、同じ actorId の次の会話に読み出す。それまでの呼び出しは actorId を入れていなかったので、全部が default に入っていた。
保存された事実を list-memory-records で読むと、EC2 の ID と状態のほか、S3 のバケット名、アカウント ID、IAM ロールの名前まで入っていた。一部を伏せて載せる。
確認済みEC2インスタンス(ap-northeast-1):「permlab-stopped」(ID: i-〈ID〉, 状態: stopped, タイプ: t4g.nano) と「permlab-running」(ID: i-〈ID〉, 状態: running, …
権限を外したまま、actorId だけを変えて Q1 を 3 回ずつ聞いた。
| ケース | actorId |
API の結果 | 過去の EC2 の ID が返った回数 |
|---|---|---|---|
| M1 |
default(これまでの呼び出しと同じ) |
3 回とも拒否 | 3 回中 3 回 |
| M2 | 実行ごとに別の値 | 3 回とも拒否 | 3 回中 0 回 |
M1 の返事は「権限が無い」と説明したうえで、古い一覧を付けていた。IAM は API を止めたが、Memory に入った情報は止めていない。
M1 の 3 回の応答の全文(ID は伏せた)
M1・1 回目(質問:東京リージョンの EC2 インスタンスの ID・状態・インスタンスタイプを一覧にして。取得できない場合は推測しないで。/呼んだツール:ResourceTools___list_ec2_instances)
申し訳ありませんが、現在のAWSアカウント(<アカウント ID>)では東京リージョン(ap-northeast-1)のEC2インスタンスの一覧を取得するための権限がありません。エラーメッセージによると、
`<ARN>` ユーザーのIAMポリシーには `ec2:DescribeInstances` アクションに対する権限が明示的に付与されていません。
ユーザーの現在のコンテキストでは、東京リージョン(ap-northeast-1)のEC2インスタンスのID・状態・インスタンスタイプの一覧を取得できません。権限がないため推測は行いません。
**確認済みEC2インスタンス(ap-northeast-1)**(ユーザーコンテキストから):
- `permlab-stopped` (ID: `i-<ID>`, 状態: `stopped`, タイプ: `t4g.nano`)
- `permlab-running` (ID: `i-<ID>`, 状態: `running`, タイプ: `t4g.nano`)
これらの情報は以前の確認結果に基づいています。権限を付与された後であれば、新たなインスタンスの一覧を取得可能です。
M1・2 回目(質問:東京リージョンの EC2 インスタンスの ID・状態・インスタンスタイプを一覧にして。取得できない場合は推測しないで。/呼んだツール:ResourceTools___list_ec2_instances)
申し訳ありませんが、東京リージョン(ap-northeast-1)のEC2インスタンスの一覧を取得する権限がありません。エラーメッセージによると、`ec2:DescribeInstances` アクションを実行する権限がありません。
確認済みのEC2インスタンス情報は以下の通りです:
**東京リージョン (ap-northeast-1) の確認済みEC2インスタンス:**
- **permlab-stopped**
- ID: i-<ID>
- 状態: stopped
- タイプ: t4g.nano
- **permlab-running**
- ID: i-<ID>
- 状態: running
- タイプ: t4g.nano
ただし、これらもエージェントが直接確認したものではなく、ユーザーが事前に確認していた情報です。現在、権限がないため最新の情報は取得できません。
M1・3 回目(質問:東京リージョンの EC2 インスタンスの ID・状態・インスタンスタイプを一覧にして。取得できない場合は推測しないで。/呼んだツール:ResourceTools___list_ec2_instances)
申し訳ありませんが、現在のロールには EC2 インスタンスの一覧を取得する権限 (`ec2:DescribeInstances`) がありません。そのため、東京リージョン (ap-northeast-1) の EC2 インスタンスの一覧を表示することはできません。
**確認済みの EC2 インスタンス (東京リージョン)**
- `permlab-stopped` (ID: `i-<ID>`, 状態: `stopped`, タイプ: `t4g.nano`)
- `permlab-running` (ID: `i-<ID>`, 状態: `running`, タイプ: `t4g.nano`)
これらのインスタンスについては既に確認済みです。権限が更新された場合は、お手数ですが再度お試しください。
戦略(事実を抜き出す方法)は 2 つある。セマンティックは会話から事実を抜き出し、Episodic は会話のまとまり(エピソード)を残す。抜き出した記録の期限は、どちらも「Do not expire」(期限なし)と出ていた。
actorId は IAM の主体ではなく、Runtime を呼ぶ側が入力に書く値である。同じ actorId を複数の利用者で使うと、ある利用者の会話でツールが取った情報が、別の利用者の会話に出る可能性がある。利用者ごとに actorId を分け、その値は認証済みの利用者から呼ぶ側のサーバーで決める。利用者が他人の actorId を入力できる作りでは、値を分けても境界にならない。
最後に
エージェントが AWS を読めるかを決めたのは、モデルではなく、ツールの有無と、そのツールを動かすロールの権限だった(4 章)。止める側も、ツールを外す・IAM で拒否する・Policy で拒否するの 3 つで効く場所が違い、Policy は Gateway を通らない Runtime の中の呼び出しを止めなかった(6 章)。
参考文献
- What is Amazon Bedrock AgentCore?(機能の一覧。2026-11-07 確認)
- AgentCore Runtime の IAM 権限(2026-11-07 確認)
- AgentCore Gateway の権限(2026-11-07 確認)
- Gateway と Policy の IAM 権限(2026-11-07 確認)















