[Update] I tried Amazon Bedrock AgentCore Runtime after it added support for resource-based policies

[Update] I tried Amazon Bedrock AgentCore Runtime after it added support for resource-based policies

2025.12.14

This page has been translated by machine translation. View original

Introduction

Hello, this is Kamino from the consulting department who loves La Moo.
While enthusiastically working with Amazon Bedrock AgentCore Runtime, I suddenly noticed that resource-based policies had been added.

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

While wondering if there had been such an update, I searched the official documentation and found a page mentioning it.

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

Wow, really? It seems they quietly added support for Runtime and Gateway...
Let's immediately check how it works with Runtime!

What's now possible

Until now, resource-based policies couldn't be configured, so
even if you used a proxy like API Gateway + Lambda, anyone with IAM user execution permissions could execute it.

CleanShot 2025-12-13 at 22.24.18@2x

Now that resource-based permissions can be granted, we can more strictly limit who can execute the runtime.
It looks something like this:

CleanShot 2025-12-13 at 22.29.11@2x

Being able to narrow down the execution source more precisely is beneficial in cases like when you're proxying AgentCore executions. Let's try it out!

Preparation

I'm using the following versions:

  • CDK: 2.230.0
  • Node.js v24.10.0

Let's set up resources using pre-prepared CDK code.

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

To immediately test resource-based policies, I'll create a full set of Lambda + API Gateway + AgentCore using CDK.
The Lambda function's execution role will have permissions to execute AgentCore.

Create a directory and clone the source code:

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

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

cd agentcore-apigw-streaming-cdk

# Install libraries
bun install

Let's proceed with deployment:

# First-time execution only
cdk bootstrap

# Deploy
cdk deploy

After deployment completes, the following parameters will be displayed:

# Execution result
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>

Please note the following values from your environment as they will be used in subsequent steps:

  • ApiGatewayInvokeUrl
    • API Gateway invocation URL, used for making calls later
  • ProxyFunctionRoleArn
    • Lambda function's IAM role ARN (used for resource-based policy)
  • RuntimeArn
    • AgentCore Runtime ARN

Let's perform a test to verify functionality.
First, let's confirm that we can execute the Runtime via API Gateway:

# Replace <ApiGatewayInvokeUrl> with the URL output during deployment
curl --no-buffer -X POST <ApiGatewayInvokeUrl> \
  -H "Content-Type: application/json" \
  -d '{"prompt":"こんにちは、あなたは何ができますか?"}'

Execution Result
# Execution result
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}}}}

Execution via API Gateway was successful!
Next, let's test from the console.
Since the logged-in IAM user has Admin permissions, we expect it to work without issues.
To invoke it, select Test > Agent Sandbox and choose the Runtime we created.

CleanShot 2025-12-13 at 23.45.00@2x

Enter the following prompt for testing:

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

CleanShot 2025-12-13 at 19.21.39@2x

This also executed without issue!
Now that execution works, let's set up policy-based permissions to ensure it can only be called through Lambda.

Setting Up Resource-Based Policy Permissions

Let's directly edit the settings of the Runtime we created in the console.
Select the Runtime name created by CDK.

CleanShot 2025-12-13 at 22.54.01@2x

After selecting the Runtime, choose the DEFAULT endpoint.

CleanShot 2025-12-13 at 22.54.18@2x

Select the Add button in the Resource-based policy section. It seems resource-based policies can be configured per endpoint, which allows for different permission levels in different environments.

CleanShot 2025-12-13 at 22.54.26@2x

Enter a policy that specifies the previously noted Lambda function role ARN and denies execution for anything else.
After entering it, select the Save button.

{
  "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>"
        }
      }
    }
  ]
}

Replace the placeholders with the following values:

  • <region>: The deployed region (e.g., us-west-2)
  • <account-id>: Your AWS account ID
  • <runtime-id>: The RuntimeId output during deployment
  • <ProxyFunctionRoleArn>: The ProxyFunctionRoleArn output during deployment

CleanShot 2025-12-13 at 23.07.19@2x

Now that the permissions are set, let's try again.
First, let's execute via API Gateway + Lambda:

curl --no-buffer -X POST <ApiGatewayInvokeUrl> \
  -H "Content-Type: application/json" \
  -d '{"prompt":"こんにちは、あなたは何ができますか?"}'
Execution Result
# Execution result
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}}}}

This executed without issues! Now let's try from the console, which should be denied because it uses user permissions.
Let's test with the same prompt as before:

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

CleanShot 2025-12-13 at 23.09.52@2x

The resource-based policy was evaluated and the request was denied!
As expected, execution is now only possible through Lambda.

Conclusion

I tried out the newly added resource-based IAM policy control for Amazon Bedrock AgentCore Runtime!
This update offers more options for cases where you want to proxy or more strictly limit execution sources. I hope those who needed this feature find it useful.

I hope this article was helpful! Thanks for reading to the end!

Additional Note: Important Points from the Official Documentation

In this test, I used minimal policies since our main goal was to "only allow execution via Lambda", but
reading the official documentation revealed several important points.

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

Here are some key points worth highlighting:

Both Runtime/Endpoint and Gateway are Eligible

Resource-based policies can be used not only for Runtime but also for Gateway.
For Gateway, use cases might include allowing only specific Runtimes.

Policy Syntax Differs Based on Authentication Method (SigV4 vs OAuth)

This seems subtly important to remember:

  • SigV4: Principal can specify IAM users/roles/accounts
  • OAuth: Principal must be fixed as "*" (wildcard required), and instead conditions like aws:SourceVpc or aws:SourceVpce must be used to restrict access

Actions That Can Be Restricted by Policy-Based Controls

In this example, we targeted bedrock-agentcore:InvokeAgentRuntime, but there are multiple actions available:

  • InvokeAgentRuntime
  • InvokeAgentRuntimeWithWebSocketStream
  • InvokeAgentRuntimeForUser (when passing user id in headers)
  • InvokeAgentRuntimeWithWebSocketStreamForUser (when passing user id in headers)
  • bedrock-agentcore:StopRuntimeSession (for stopping sessions)
  • bedrock-agentcore:GetAgentCard (for retrieving agent card information, used with A2A)
  • bedrock-agentcore:InvokeGateway (for invoking Gateway)

To avoid cases where one path is blocked but another remains open, you should check which permissions apply to your calling methods.

Deny Takes Precedence in Policy Evaluation

While not unique to AgentCore (similar to resource-based policies in Lambda, etc.), Deny takes precedence:

  • If there's an explicit Deny anywhere, it overrides any Allow
  • If there's no Deny, an Allow in either IAM or resource-based policy is sufficient
  • If neither has anything specified, access is denied

I'm quoting the table from the official documentation. Since this can get complex, it's important to decide on your IAM permission design early.

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

Share this article

FacebookHatena blogX

Related articles