[アップデート] Amazon Bedrock AgentCore Runtimeがリソースベースポリシーに対応していたので試してみた

[アップデート] Amazon Bedrock AgentCore Runtimeがリソースベースポリシーに対応していたので試してみた

2025.12.14

はじめに

こんにちは、ラ・ムーが大好きなコンサルティング部の神野です。
元気よくAmazon Bedrock AgentCore Runtimeを触っていたら、ふとリソースベースのポリシーが生えていることに気づきました。

https://x.com/yjinn448208/status/1998658025234784760?s=20

そんなアップデートあったかなーと考えつつ、公式ドキュメントを漁っていると記載があるページを見つけました。

https://docs.aws.amazon.com/ja_jp/bedrock-agentcore/latest/devguide/resource-based-policies.html

おおお、まじか、しれっとRuntimeとGatewayに対応していたんですね・・・
早速挙動をRuntimeで確認してみたいと思います!

何ができるようになったか

今までリソースベースのポリシーを設定できなかったので、
API Gateway + Lambdaなどでプロキシしたとしても、IAMユーザーなどが実行権限があれば誰でも実行できてしまう状態でした。

CleanShot 2025-12-13 at 22.24.18@2x

それが今回、リソースベースの権限が付与できるようになったことで実行元をより限定できるようになりました。
下記のようなイメージです。

CleanShot 2025-12-13 at 22.29.11@2x

より実行元を絞れるのは上記のように、AgentCoreの実行をプロキシなどしている場合は好ましいケースもありますよね。
早速試してみます。

準備

Node.js、CDKのバージョンは下記を使用しています。

  • CDK:2.230.0
  • Node.js v24.10.0

事前に準備したCDKコードでリソースを準備します。

https://github.com/yuu551/agentcore-apigw-streaming-cdk

すぐにリソースベースのポリシーを試せるようLambda + API Gateway + AgentCore一式をCDKで作成します。
Lambda関数の実行ロールはAgentCoreが実行できる権限を付与しておきます。

任意のディレクトリを作成して、ソースコードをCloneしていきます。

mkdir resource-policy-sample
cd resource-policy-sample

git clone https://github.com/yuu551/agentcore-apigw-streaming-cdk

cd agentcore-apigw-streaming-cdk

# ライブラリインストール
bun install

早速デプロイ進めていきます。

# 初回実行のみ
cdk bootstrap

# デプロイ
cdk deploy

デプロイが完了したら下記パラメータが表示されます。

# 実行結果
Outputs:
AgentCoreProxyStack.AgentCoreProxyAPIEndpoint9D0E07BE = https://<api-id>.execute-api.<region>.amazonaws.com/prod/
AgentCoreProxyStack.ApiGatewayInvokeUrl = https://<api-id>.execute-api.<region>.amazonaws.com/prod/invoke
AgentCoreProxyStack.ApiGatewayUrl = https://<api-id>.execute-api.<region>.amazonaws.com/prod/
AgentCoreProxyStack.ProxyFunctionArn = arn:aws:lambda:<region>:<account-id>:function:agentcore-proxy
AgentCoreProxyStack.ProxyFunctionName = agentcore-proxy
AgentCoreProxyStack.ProxyFunctionRoleArn = arn:aws:iam::<account-id>:role/AgentCoreProxyStack-AgentCoreProxyFunctionRole4FDE4-<suffix>
AgentCoreProxyStack.RuntimeArn = arn:aws:bedrock-agentcore:<region>:<account-id>:runtime/<runtime-name>-<suffix>
AgentCoreProxyStack.RuntimeId = <runtime-name>-<suffix>
AgentCoreProxyStack.RuntimeName = <runtime-name>

以下の値は後続の手順で使用するので、ご自身の環境の値をメモしておいてください。

  • ApiGatewayInvokeUrl
    • API Gatewayの呼び出しURL、この後に呼び出す際に使用
  • ProxyFunctionRoleArn
    • Lambda関数のIAMロールARN(リソースベースポリシーで使用)
  • RuntimeArn
    • AgentCore RuntimeのARN

動作確認のテストをしてみます。
まずはAPI Gateway経由でRuntimeを実行できるか確認してみます。

# <ApiGatewayInvokeUrl>にはデプロイ時に出力されたURLを入力してください
curl --no-buffer -X POST <ApiGatewayInvokeUrl> \
  -H "Content-Type: application/json" \
  -d '{"prompt":"こんにちは、あなたは何ができますか?"}'

実行結果
# 実行結果
data: {"event": {"messageStart": {"role": "assistant"}}}

data: {"event": {"contentBlockDelta": {"delta": {"text": "こんにち"}, "contentBlockIndex": 0}}}

data: {"event": {"contentBlockDelta": {"delta": {"text": "は!"}, "contentBlockIndex": 0}}}

data: {"event": {"contentBlockDelta": {"delta": {"text": "私は"}, "contentBlockIndex": 0}}}

data: {"event": {"contentBlockDelta": {"delta": {"text": "天"}, "contentBlockIndex": 0}}}

data: {"event": {"contentBlockDelta": {"delta": {"text": "気情"}, "contentBlockIndex": 0}}}

data: {"event": {"contentBlockDelta": {"delta": {"text": "報を調"}, "contentBlockIndex": 0}}}

data: {"event": {"contentBlockDelta": {"delta": {"text": "べること"}, "contentBlockIndex": 0}}}

data: {"event": {"contentBlockDelta": {"delta": {"text": "ができ"}, "contentBlockIndex": 0}}}

data: {"event": {"contentBlockDelta": {"delta": {"text": "る"}, "contentBlockIndex": 0}}}

data: {"event": {"contentBlockDelta": {"delta": {"text": "A"}, "contentBlockIndex": 0}}}

data: {"event": {"contentBlockDelta": {"delta": {"text": "Iアシス"}, "contentBlockIndex": 0}}}

data: {"event": {"contentBlockDelta": {"delta": {"text": "タントです。"}, "contentBlockIndex": 0}}}

data: {"event": {"contentBlockDelta": {"delta": {"text": "特"}, "contentBlockIndex": 0}}}

data: {"event": {"contentBlockDelta": {"delta": {"text": "定の都"}, "contentBlockIndex": 0}}}

data: {"event": {"contentBlockDelta": {"delta": {"text": "市の現在"}, "contentBlockIndex": 0}}}

data: {"event": {"contentBlockDelta": {"delta": {"text": "の天気を"}, "contentBlockIndex": 0}}}

data: {"event": {"contentBlockDelta": {"delta": {"text": "確認した"}, "contentBlockIndex": 0}}}

data: {"event": {"contentBlockDelta": {"delta": {"text": "い"}, "contentBlockIndex": 0}}}

data: {"event": {"contentBlockDelta": {"delta": {"text": "場合は、"}, "contentBlockIndex": 0}}}

data: {"event": {"contentBlockDelta": {"delta": {"text": "お"}, "contentBlockIndex": 0}}}

data: {"event": {"contentBlockDelta": {"delta": {"text": "気"}, "contentBlockIndex": 0}}}

data: {"event": {"contentBlockDelta": {"delta": {"text": "軽にお"}, "contentBlockIndex": 0}}}

data: {"event": {"contentBlockDelta": {"delta": {"text": "尋ね"}, "contentBlockIndex": 0}}}

data: {"event": {"contentBlockDelta": {"delta": {"text": "ください。例"}, "contentBlockIndex": 0}}}

data: {"event": {"contentBlockDelta": {"delta": {"text": "えば、「"}, "contentBlockIndex": 0}}}

data: {"event": {"contentBlockDelta": {"delta": {"text": "東"}, "contentBlockIndex": 0}}}

data: {"event": {"contentBlockDelta": {"delta": {"text": "京の天"}, "contentBlockIndex": 0}}}

data: {"event": {"contentBlockDelta": {"delta": {"text": "気を教え"}, "contentBlockIndex": 0}}}

data: {"event": {"contentBlockDelta": {"delta": {"text": "て」や"}, "contentBlockIndex": 0}}}

data: {"event": {"contentBlockDelta": {"delta": {"text": "「"}, "contentBlockIndex": 0}}}

data: {"event": {"contentBlockDelta": {"delta": {"text": "ニ"}, "contentBlockIndex": 0}}}

data: {"event": {"contentBlockDelta": {"delta": {"text": "ューヨーク"}, "contentBlockIndex": 0}}}

data: {"event": {"contentBlockDelta": {"delta": {"text": "の天気は"}, "contentBlockIndex": 0}}}

data: {"event": {"contentBlockDelta": {"delta": {"text": "どうですか"}, "contentBlockIndex": 0}}}

data: {"event": {"contentBlockDelta": {"delta": {"text": "?"}, "contentBlockIndex": 0}}}

data: {"event": {"contentBlockDelta": {"delta": {"text": "」の"}, "contentBlockIndex": 0}}}

data: {"event": {"contentBlockDelta": {"delta": {"text": "ような"}, "contentBlockIndex": 0}}}

data: {"event": {"contentBlockDelta": {"delta": {"text": "質問に"}, "contentBlockIndex": 0}}}

data: {"event": {"contentBlockDelta": {"delta": {"text": "お答えでき"}, "contentBlockIndex": 0}}}

data: {"event": {"contentBlockDelta": {"delta": {"text": "ます。\n\nまた"}, "contentBlockIndex": 0}}}

data: {"event": {"contentBlockDelta": {"delta": {"text": "、天気に"}, "contentBlockIndex": 0}}}

data: {"event": {"contentBlockDelta": {"delta": {"text": "関する"}, "contentBlockIndex": 0}}}

data: {"event": {"contentBlockDelta": {"delta": {"text": "簡単な質"}, "contentBlockIndex": 0}}}

data: {"event": {"contentBlockDelta": {"delta": {"text": "問にも対"}, "contentBlockIndex": 0}}}

data: {"event": {"contentBlockDelta": {"delta": {"text": "応できます。"}, "contentBlockIndex": 0}}}

data: {"event": {"contentBlockDelta": {"delta": {"text": "ど"}, "contentBlockIndex": 0}}}

data: {"event": {"contentBlockDelta": {"delta": {"text": "の"}, "contentBlockIndex": 0}}}

data: {"event": {"contentBlockDelta": {"delta": {"text": "ようなお"}, "contentBlockIndex": 0}}}

data: {"event": {"contentBlockDelta": {"delta": {"text": "手"}, "contentBlockIndex": 0}}}

data: {"event": {"contentBlockDelta": {"delta": {"text": "伝いができ"}, "contentBlockIndex": 0}}}

data: {"event": {"contentBlockDelta": {"delta": {"text": "る"}, "contentBlockIndex": 0}}}

data: {"event": {"contentBlockDelta": {"delta": {"text": "か"}, "contentBlockIndex": 0}}}

data: {"event": {"contentBlockDelta": {"delta": {"text": "、お"}, "contentBlockIndex": 0}}}

data: {"event": {"contentBlockDelta": {"delta": {"text": "聞かせください"}, "contentBlockIndex": 0}}}

data: {"event": {"contentBlockDelta": {"delta": {"text": "。どん"}, "contentBlockIndex": 0}}}

data: {"event": {"contentBlockDelta": {"delta": {"text": "な都"}, "contentBlockIndex": 0}}}

data: {"event": {"contentBlockDelta": {"delta": {"text": "市の天気"}, "contentBlockIndex": 0}}}

data: {"event": {"contentBlockDelta": {"delta": {"text": "情"}, "contentBlockIndex": 0}}}

data: {"event": {"contentBlockDelta": {"delta": {"text": "報で"}, "contentBlockIndex": 0}}}

data: {"event": {"contentBlockDelta": {"delta": {"text": "もす"}, "contentBlockIndex": 0}}}

data: {"event": {"contentBlockDelta": {"delta": {"text": "ぐに調"}, "contentBlockIndex": 0}}}

data: {"event": {"contentBlockDelta": {"delta": {"text": "べること"}, "contentBlockIndex": 0}}}

data: {"event": {"contentBlockDelta": {"delta": {"text": "ができます。何"}, "contentBlockIndex": 0}}}

data: {"event": {"contentBlockDelta": {"delta": {"text": "か特"}, "contentBlockIndex": 0}}}

data: {"event": {"contentBlockDelta": {"delta": {"text": "別な都"}, "contentBlockIndex": 0}}}

data: {"event": {"contentBlockDelta": {"delta": {"text": "市の天気"}, "contentBlockIndex": 0}}}

data: {"event": {"contentBlockDelta": {"delta": {"text": "が"}, "contentBlockIndex": 0}}}

data: {"event": {"contentBlockDelta": {"delta": {"text": "知"}, "contentBlockIndex": 0}}}

data: {"event": {"contentBlockDelta": {"delta": {"text": "りたい"}, "contentBlockIndex": 0}}}

data: {"event": {"contentBlockDelta": {"delta": {"text": "ですか?"}, "contentBlockIndex": 0}}}

data: {"event": {"contentBlockStop": {"contentBlockIndex": 0}}}

data: {"event": {"messageStop": {"stopReason": "end_turn"}}}

data: {"event": {"metadata": {"usage": {"inputTokens": 429, "outputTokens": 187, "totalTokens": 616}, "metrics": {"latencyMs": 3213}}}}

API Gateway経由からの実行は問題なく成功しました!
次はコンソール上からテストしてみます。
ログインしているIAMユーザーはAdmin権限を持っているため問題なく実行できる想定です。
呼び出す際は Test > Agent Sandboxを選択して、今回作成したRuntimeを選びます。

CleanShot 2025-12-13 at 23.45.00@2x

下記プロンプトを入力してテストします。

{"prompt":"こんにちは、あなたは何ができますか?"}

CleanShot 2025-12-13 at 19.21.39@2x

こちらも問題なく実行できました!
今は実行できましたが、ポリシーベースの権限設定を行なってLambda経由以外は呼び出せないようにします。

ポリシーベースの権限設定をやってみる

コンソールで作成したRuntimeの設定を直接編集してみます。
CDKで作成したRuntime名を選択します。

CleanShot 2025-12-13 at 22.54.01@2x

Runtimeを選択したら、DEFAULTエンドポイントを選択します。

CleanShot 2025-12-13 at 22.54.18@2x

Resource-based policyの箇所のAddボタンを選択します。Resource-based policyはエンドポイント単位で設定できる値なんですね。環境ごとに権限の強度なども考えられそうです。

CleanShot 2025-12-13 at 22.54.26@2x

事前にメモした、Lambda関数のロールARNを指定して、該当のLambda関数以外は実行できないポリシーを入力します。
入力できたらSaveボタンを選択します。

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "DenyAllOthers",
      "Effect": "Deny",
      "Principal": "*",
      "Action": "bedrock-agentcore:InvokeAgentRuntime",
      "Resource": "arn:aws:bedrock-agentcore:<region>:<account-id>:runtime/<runtime-id>/runtime-endpoint/DEFAULT",
      "Condition": {
        "StringNotEquals": {
          "aws:PrincipalArn": "<ProxyFunctionRoleArn>"
        }
      }
    }
  ]
}

各プレースホルダーには以下の値を入力してください。

  • <region>: デプロイしたリージョン(例: us-west-2)
  • <account-id>: AWSアカウントID
  • <runtime-id>: デプロイ時に出力されたRuntimeId
  • <ProxyFunctionRoleArn>: デプロイ時に出力されたProxyFunctionRoleArn

CleanShot 2025-12-13 at 23.07.19@2x

これで権限が設定できたので再度試してみます。
まずはAPI Gateway + Lambda経由で実行してみます。

curl --no-buffer -X POST <ApiGatewayInvokeUrl> \
  -H "Content-Type: application/json" \
  -d '{"prompt":"こんにちは、あなたは何ができますか?"}'
実行結果
# 実行結果
data: {"event": {"messageStart": {"role": "assistant"}}}

data: {"event": {"contentBlockDelta": {"delta": {"text": "こ"}, "contentBlockIndex": 0}}}

data: {"event": {"contentBlockDelta": {"delta": {"text": "んにちは!"}, "contentBlockIndex": 0}}}

data: {"event": {"contentBlockDelta": {"delta": {"text": "私は、天気情"}, "contentBlockIndex": 0}}}

data: {"event": {"contentBlockDelta": {"delta": {"text": "報を取得するお"}, "contentBlockIndex": 0}}}

data: {"event": {"contentBlockDelta": {"delta": {"text": "手伝いができる"}, "contentBlockIndex": 0}}}

data: {"event": {"contentBlockDelta": {"delta": {"text": "AIアシス"}, "contentBlockIndex": 0}}}

data: {"event": {"contentBlockDelta": {"delta": {"text": "タントです。例"}, "contentBlockIndex": 0}}}

data: {"event": {"contentBlockDelta": {"delta": {"text": "えば、特定の都"}, "contentBlockIndex": 0}}}

data: {"event": {"contentBlockDelta": {"delta": {"text": "市の現在の天気"}, "contentBlockIndex": 0}}}

data: {"event": {"contentBlockDelta": {"delta": {"text": "を知"}, "contentBlockIndex": 0}}}

data: {"event": {"contentBlockDelta": {"delta": {"text": "りたい場"}, "contentBlockIndex": 0}}}

data: {"event": {"contentBlockDelta": {"delta": {"text": "合は、その"}, "contentBlockIndex": 0}}}

data: {"event": {"contentBlockDelta": {"delta": {"text": "都市の名"}, "contentBlockIndex": 0}}}

data: {"event": {"contentBlockDelta": {"delta": {"text": "前を教"}, "contentBlockIndex": 0}}}

data: {"event": {"contentBlockDelta": {"delta": {"text": "えていただければ"}, "contentBlockIndex": 0}}}

data: {"event": {"contentBlockDelta": {"delta": {"text": "、get"}, "contentBlockIndex": 0}}}

data: {"event": {"contentBlockDelta": {"delta": {"text": "_weatherツールを使用して"}, "contentBlockIndex": 0}}}

data: {"event": {"contentBlockDelta": {"delta": {"text": "最新の天気"}, "contentBlockIndex": 0}}}

data: {"event": {"contentBlockDelta": {"delta": {"text": "情報を提"}, "contentBlockIndex": 0}}}

data: {"event": {"contentBlockDelta": {"delta": {"text": "供いたします。"}, "contentBlockIndex": 0}}}

data: {"event": {"contentBlockDelta": {"delta": {"text": "\n\nその他にも、"}, "contentBlockIndex": 0}}}

data: {"event": {"contentBlockDelta": {"delta": {"text": "一般的な会"}, "contentBlockIndex": 0}}}

data: {"event": {"contentBlockDelta": {"delta": {"text": "話や"}, "contentBlockIndex": 0}}}

data: {"event": {"contentBlockDelta": {"delta": {"text": "お手"}, "contentBlockIndex": 0}}}

data: {"event": {"contentBlockDelta": {"delta": {"text": "伝いが"}, "contentBlockIndex": 0}}}

data: {"event": {"contentBlockDelta": {"delta": {"text": "可能です。"}, "contentBlockIndex": 0}}}

data: {"event": {"contentBlockDelta": {"delta": {"text": "何かお困"}, "contentBlockIndex": 0}}}

data: {"event": {"contentBlockDelta": {"delta": {"text": "りのことや、知"}, "contentBlockIndex": 0}}}

data: {"event": {"contentBlockDelta": {"delta": {"text": "りたい"}, "contentBlockIndex": 0}}}

data: {"event": {"contentBlockDelta": {"delta": {"text": "ことはありますか?"}, "contentBlockIndex": 0}}}

data: {"event": {"contentBlockDelta": {"delta": {"text": "どんな"}, "contentBlockIndex": 0}}}

data: {"event": {"contentBlockDelta": {"delta": {"text": "ことでも、でき"}, "contentBlockIndex": 0}}}

data: {"event": {"contentBlockDelta": {"delta": {"text": "る限りサ"}, "contentBlockIndex": 0}}}

data: {"event": {"contentBlockDelta": {"delta": {"text": "ポートさせていただきます"}, "contentBlockIndex": 0}}}

data: {"event": {"contentBlockDelta": {"delta": {"text": "。"}, "contentBlockIndex": 0}}}

data: {"event": {"contentBlockStop": {"contentBlockIndex": 0}}}

data: {"event": {"messageStop": {"stopReason": "end_turn"}}}

data: {"event": {"metadata": {"usage": {"inputTokens": 429, "outputTokens": 160, "totalTokens": 589}, "metrics": {"latencyMs": 3151}}}}

こちらは問題なく実行できましたね!今度はユーザーの権限を使用している実行できないはずであるコンソール上から呼び出してみます。
先ほどと同じ下記プロンプトを入力してテストをしてみます。

{"prompt":"こんにちは、あなたは何ができますか?"}

CleanShot 2025-12-13 at 23.09.52@2x

リソースベースのポリシーが評価されて拒否されましたね!
期待通りLambda経由以外は実行できなくなりましたね。

おわりに

Amazon Bedrock AgentCore RuntimeにリソースベースのIAMポリシー制御ができるようになっていたので試してみました!
プロキシした場合や実行元をより絞りたい場合は活用の余地があるアップデートかと思います。ぜひ欲していた方は活用してみてください。

本記事が少しでも参考になったら幸いです!最後までご覧いただきありがとうございましたー!!

補足:公式ドキュメントを読んで大事そうだなと思ったポイント

今回の検証は「Lambda経由の実行のみを許可する」が主目的だったので最低限のポリシーで試しましたが、
公式ドキュメントを読むと、いくつか注意点がありました。

https://docs.aws.amazon.com/ja_jp/bedrock-agentcore/latest/devguide/resource-based-policies.html

重要そうなポイントをピックアップして紹介します。

対象は Runtime / Endpoint と Gateway(両方)

リソースベースポリシーはRuntimeだけでなく、Gatewayでも使用できます。
Gatewayの場合は、特定のRuntimeのみ許可するなどのユースケースがありそうです。

認証方式でポリシーの書き方が変わる(SigV4 と OAuth)

地味に重要そうなので覚えておきたいです。

  • SigV4:Principal に IAMユーザー/ロール/アカウントを記載可能
  • OAuth:Principal は "*" 固定(ワイルドカード必須)で、代わりに aws:SourceVpc とか aws:SourceVpce みたいなConditionで絞る必要がある

ポリシーベースで制限可能な対象アクション

今回の例では bedrock-agentcore:InvokeAgentRuntime を対象にしましたが、対象のアクション は複数あります。

  • InvokeAgentRuntime
  • InvokeAgentRuntimeWithWebSocketStream
  • InvokeAgentRuntimeForUser(ヘッダーで user id を渡すもの)
  • InvokeAgentRuntimeWithWebSocketStreamForUser(ヘッダーで user id を渡すもの)
  • bedrock-agentcore:StopRuntimeSession (セッションを停止するもの)
  • bedrock-agentcore:GetAgentCard (エージェントカード情報を取得する、A2Aで使用する)
  • bedrock-agentcore:InvokeGateway(Gatewayを起動する)

実行できないようにしたのに別の経路が空いてたケースを避けたい場合は、
自分が使ってる呼び出し方式に合わせてどの権限が該当するか確認しておきたいです。

ポリシー評価は Denyが優先

AgentCoreに限った例ではないですが、Lambdaなどでもあるようなリソースベースのポリシーと同じくDenyが優先されます。

  • どこかに明示Denyがあると、他がAllowでもアウト
  • Denyが無いなら、IAM側かリソース側のどっちかがAllowしてればOK
  • どっちも何も無ければ拒否

公式ドキュメントからも表を引用します。複雑になりがちなのでどういったIAMの権限設計にするかは最初に決めたいポイントですね。

When a request is made to a Amazon Bedrock AgentCore resource, AWS evaluates both identity-based and resource-based policies. The following table shows how different policy combinations affect access:

IAM Policy Resource Policy Result
Grants access Silent Allowed
Grants access Grants access Allowed
Grants access Denies access Denied
Silent Silent Denied
Silent Grants access Allowed
Silent Denies access Denied
Denies access Silent Denied
Denies access Allows access Denied
Denies access Denies access Denied

この記事をシェアする

FacebookHatena blogX

関連記事