![[Update] Policy feature (preview) has been added to Amazon Bedrock AgentCore #AWSreInvent](https://devio2024-media.developers.io/image/upload/f_auto,q_auto,w_3840/v1764709867/user-gen-eyecatch/luaqxeood29sjmqwzemm.png)
[Update] Policy feature (preview) has been added to Amazon Bedrock AgentCore #AWSreInvent
This page has been translated by machine translation. View original
Introduction
Hello, I'm Jinno from the Consulting Department!
At re:Invent 2025 Keynote, it was announced that a Policy feature (Amazon Bedrock AgentCore Policy) will be added to Amazon Bedrock AgentCore in preview!
What's great is that until now, when executing Runtime→Gateway→tools, fine-grained authorization control for who can use which tools could only be achieved by creating a separate Lambda function with the recently released Interceptors.
With this update, authorization control is now possible using the Cedar policy language through this Policy feature, allowing you to control "who" can perform "which actions" on "what resources" under certain conditions. (Cedar is also used in the Amazon Verified Permissions authorization service)
Cedar is a policy language that can be written as shown below. The following policy targets authenticated users using the process_refund tool via a Gateway. It also includes conditions, such as targeting the role refund-agent and cases where the amount is less than 200.
permit(
principal is AgentCore::OAuthUser,
action == AgentCore::Action::"RefundTool__process_refund",
resource == AgentCore::Gateway::"<GATEWAY_ARN>"
)
when {
principal.hasTag("role") &&
principal.getTag("role") == "refund-agent" &&
context.input.amount < 200
};
Good news for those thinking this policy might be difficult to create - you can also create Cedar policies using natural language. By requesting in natural language through the console, the Gateway can read tool information from the connected MCP Server or Lambda functions and create appropriate policies as shown below.
I'd like order information to be viewable, but updates and creation to be prohibited.

The permit part allows reference operations, and the forbid part prohibits creation and updates. While it's unclear how much can be handled with natural language, it's convenient for creating initial drafts.
In this article, I'd like to share my quick test of the policy evaluation feature!
Prerequisites
For this test, I'll follow the "Getting started with AgentCore Policy" from the official documentation to check how it works.
Let's Try It
First, let's create a directory and activate a virtual environment.
mkdir agentcore-policy-quickstart
cd agentcore-policy-quickstart
python3 -m venv .venv
source .venv/bin/activate
Then we'll install the dependencies.
We'll use bedrock-agentcore-starter-toolkit version 0.21, which supports Policy.
pip install boto3
pip install bedrock-agentcore-starter-toolkit=0.21
pip install requests
Next, let's create a script setup_policy.py to create the Gateway, Cognito, and Policy.
"""
Setup script to create Gateway with Policy Engine
Run this first: python setup_policy.py
"""
from bedrock_agentcore_starter_toolkit.operations.gateway.client import GatewayClient
from bedrock_agentcore_starter_toolkit.operations.policy.client import PolicyClient
from bedrock_agentcore_starter_toolkit.utils.lambda_utils import create_lambda_function
import boto3
import json
import logging
import time
def setup_policy():
# Configuration
region = "us-west-2"
refund_limit = 1000
print("🚀 Setting up AgentCore Gateway with Policy Engine...")
print(f"Region: {region}\n")
# Initialize clients
gateway_client = GatewayClient(region_name=region)
gateway_client.logger.setLevel(logging.INFO)
policy_client = PolicyClient(region_name=region)
policy_client.logger.setLevel(logging.INFO)
# Step 1: Create OAuth authorizer
print("Step 1: Creating OAuth authorization server...")
cognito_response = gateway_client.create_oauth_authorizer_with_cognito("PolicyGateway")
print("✓ Authorization server created\n")
# Step 2: Create Gateway
print("Step 2: Creating Gateway...")
gateway = gateway_client.create_mcp_gateway(
name=None,
role_arn=None,
authorizer_config=cognito_response["authorizer_config"],
enable_semantic_search=False,
)
print(f"✓ Gateway created: {gateway['gatewayUrl']}\n")
# Fix IAM permissions
gateway_client.fix_iam_permissions(gateway)
print("⏳ Waiting 30s for IAM propagation...")
time.sleep(30)
print("✓ IAM permissions configured\n")
# Step 3: Create Lambda function with refund tool
print("Step 3: Creating Lambda function with refund tool...")
refund_lambda_code = """
def lambda_handler(event, context):
amount = event.get('amount', 0)
return {
"status": "success",
"message": f"Refund of ${amount} processed successfully",
"amount": amount
}
"""
session = boto3.Session(region_name=region)
lambda_arn = create_lambda_function(
session=session,
logger=gateway_client.logger,
function_name=f"RefundTool-{int(time.time())}",
lambda_code=refund_lambda_code,
runtime="python3.13",
handler="lambda_function.lambda_handler",
gateway_role_arn=gateway["roleArn"],
description="Refund tool for policy demo",
)
print("✓ Lambda function created\n")
# Step 4: Add Lambda target with refund tool schema
print("Step 4: Adding Lambda target with refund tool schema...")
lambda_target = gateway_client.create_mcp_gateway_target(
gateway=gateway,
name="RefundTarget",
target_type="lambda",
target_payload={
"lambdaArn": lambda_arn,
"toolSchema": {
"inlinePayload": [
{
"name": "process_refund",
"description": "Process a customer refund",
"inputSchema": {
"type": "object",
"properties": {
"amount": {
"type": "integer",
"description": "Refund amount in dollars"
}
},
"required": ["amount"],
},
}
]
},
},
credentials=None,
)
print("✓ Lambda target added\n")
# Step 5: Create Policy Engine
print("Step 5: Creating Policy Engine...")
engine = policy_client.create_or_get_policy_engine(
name="RefundPolicyEngine",
description="Policy engine for refund governance"
)
print(f"✓ Policy Engine: {engine['policyEngineId']}\n")
# Step 6: Create Cedar policy
print(f"Step 6: Creating Cedar policy (refund limit: ${refund_limit})...")
cedar_statement = (
f"permit(principal, "
f'action == AgentCore::Action::"RefundTarget___process_refund", '
f'resource == AgentCore::Gateway::"{gateway["gatewayArn"]}") '
f"when {{ context.input.amount < {refund_limit} }};"
)
policy = policy_client.create_or_get_policy(
policy_engine_id=engine["policyEngineId"],
name="refund_limit_policy",
description=f"Allow refunds under ${refund_limit}",
definition={"cedar": {"statement": cedar_statement}},
)
print(f"✓ Policy: {policy['policyId']}\n")
# Step 7: Attach Policy Engine to Gateway
print("Step 7: Attaching Policy Engine to Gateway (ENFORCE mode)...")
gateway_client.update_gateway_policy_engine(
gateway_identifier=gateway["gatewayId"],
policy_engine_arn=engine["policyEngineArn"],
mode="ENFORCE"
)
print("✓ Policy Engine attached to Gateway\n")
# Step 8: Save configuration
config = {
"gateway_url": gateway["gatewayUrl"],
"gateway_id": gateway["gatewayId"],
"gateway_arn": gateway["gatewayArn"],
"policy_engine_id": engine["policyEngineId"],
"policy_engine_arn": engine["policyEngineArn"],
"policy_id": policy["policyId"],
"region": region,
"client_info": cognito_response["client_info"],
"refund_limit": refund_limit
}
with open("config.json", "w") as f:
json.dump(config, f, indent=2)
print("=" * 60)
print("✅ Setup complete!")
print(f"Gateway URL: {gateway['gatewayUrl']}")
print(f"Policy Engine ID: {engine['policyEngineId']}")
print(f"Refund limit: ${refund_limit}")
print("\nConfiguration saved to: config.json")
print("\nNext step: Run 'python test_policy.py' to test your Policy")
print("=" * 60)
return config
if __name__ == "__main__":
setup_policy()
Here are the resources being created:
- Cognito
- Lambda function for MCP tool
- Cedar policy creation
- AgentCore Gateway
- Inbound Auth configuration (Cognito)
- Lambda function connected as a target
The created policy is as follows. For testing purposes, it allows anyone to execute, but limits the tool to process_refund and targets only the Gateway created in this session.
The key feature is the condition that specifies this action can only be executed when the amount is less than the refund_limit. This prevents approving refunds that exceed the refund limit.
permit(
principal,
action == AgentCore::Action::"RefundTarget___process_refund",
resource == AgentCore::Gateway::"{gateway["gatewayArn"]}"
)
when {
context.input.amount < {refund_limit}
};
As a note, the Policy's Enforcement mode is set to ENFORCE, which means requests that don't comply with the policy will be rejected.
The console shows this note:

Enabling enforcement: Consider testing and validating policies in log only mode before enabling enforcement to avoid unintended denials or adversely affecting production traffic.
It recommends testing policies in log-only mode before enabling enforcement. It's important to check that your design works as intended. This is a critical point to consider before production use.
If you want to use log-only mode, you would specify LOG_ONLY.
For now, let's proceed with ENFORCE mode.
python setup_policy.py
🚀 Setting up AgentCore Gateway with Policy Engine...
Region: us-west-2
Step 1: Creating OAuth authorization server...
2025-12-03 05:25:23,089 - bedrock_agentcore.gateway - INFO - Starting EZ Auth setup: Creating Cognito resources...
2025-12-03 05:25:24,275 - bedrock_agentcore.gateway - INFO - ✓ Created User Pool: us-west-2_1B02xRJuB
2025-12-03 05:25:25,389 - bedrock_agentcore.gateway - INFO - ✓ Created domain: agentcore-2aa795bb
2025-12-03 05:25:25,390 - bedrock_agentcore.gateway - INFO - ⏳ Waiting for domain to be available...
2025-12-03 05:25:25,567 - bedrock_agentcore.gateway - INFO - ✓ Domain is active
2025-12-03 05:25:26,122 - bedrock_agentcore.gateway - INFO - ✓ Created resource server: PolicyGateway
2025-12-03 05:25:26,526 - bedrock_agentcore.
・・・
============================================================
✅ Setup complete!
Gateway URL: https://testgateway48856501-3oq3878x1q.gateway.bedrock-agentcore.us-west-2.amazonaws.com/mcp
Policy Engine ID: RefundPolicyEngine-dxtmb2vcin
Refund limit: $1000
Configuration saved to: config.json
Next step: Run 'python test_policy.py' to test your Policy
============================================================
When you see "Setup complete", you're good to go! The refund limit is set to $1000.
Now, let's create a test script to verify with refund amounts under $1000 and over $1000.
Create a file called test_policy.py:
"""
Test Policy Engine with direct HTTP calls to Gateway
Run after setup: python test_policy.py
"""
import json
import sys
import requests
from bedrock_agentcore_starter_toolkit.operations.gateway.client import GatewayClient
def test_refund(gateway_url, bearer_token, amount):
"""Test a refund request - print raw response"""
response = requests.post(
gateway_url,
headers={
"Content-Type": "application/json",
"Authorization": f"Bearer {bearer_token}",
},
json={
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "RefundTarget___process_refund",
"arguments": {"amount": amount}
},
},
)
print(f"Status Code: {response.status_code}")
print(f"Response Body: {json.dumps(response.json(), indent=2)}")
return response
def main():
print("=" * 60)
print("🧪 Testing Policy Engine")
print("=" * 60 + "\n")
# Load configuration
try:
with open("config.json", "r") as f:
config = json.load(f)
except FileNotFoundError:
print("❌ Error: config.json not found!")
print("Please run 'python setup_policy.py' first.")
sys.exit(1)
gateway_url = config["gateway_url"]
refund_limit = config["refund_limit"]
print(f"Gateway: {gateway_url}")
print(f"Refund limit: ${refund_limit}\n")
# Get access token
print("🔑 Getting access token...")
gateway_client = GatewayClient(region_name=config["region"])
token = gateway_client.get_access_token_for_cognito(config["client_info"])
print("✅ Token obtained\n")
# Test 1: Refund $500 (should be allowed)
print(f"📝 Test 1: Refund $500 (Expected: ALLOW)")
print("-" * 40)
test_refund(gateway_url, token, 500)
print()
# Test 2: Refund $2000 (should be denied)
print(f"📝 Test 2: Refund $2000 (Expected: DENY)")
print("-" * 40)
test_refund(gateway_url, token, 2000)
print()
print("=" * 60)
print("✅ Testing complete!")
print("=" * 60)
if __name__ == "__main__":
main()
This script gets an access token and tries to process refunds for $500 and $2000. Let's run it:
python test_policy.py
Gateway: https://xxxx-xxx.gateway.bedrock-agentcore.us-west-2.amazonaws.com/mcp
Refund limit: $1000
🔑 Getting access token...
2025-12-03 05:28:36,473 - bedrock_agentcore.gateway - INFO - Fetching test token from Cognito...
2025-12-03 05:28:36,473 - bedrock_agentcore.gateway - INFO - Attempting to connect to token endpoint: https://agentcore-xxx.auth.us-west-2.amazoncognito.com/oauth2/token
2025-12-03 05:28:37,048 - bedrock_agentcore.gateway - INFO - ✓ Got test token successfully
✅ Token obtained
📝 Test 1: Refund $500 (Expected: ALLOW)
----------------------------------------
Status Code: 200
Response Body: {
"jsonrpc": "2.0",
"id": 1,
"result": {
"isError": false,
"content": [
{
"type": "text",
"text": "{\"status\":\"success\",\"message\":\"Refund of $500 processed successfully\",\"amount\":500}"
}
]
}
}
📝 Test 2: Refund $2000 (Expected: DENY)
----------------------------------------
Status Code: 200
Response Body: {
"jsonrpc": "2.0",
"id": 1,
"error": {
"code": -32002,
"message": "Tool Execution Denied: Tool call not allowed due to policy enforcement [Policy evaluation denied due to no determining policies]"
}
}
============================================================
✅ Testing complete!
============================================================
Great! The $2000 refund was properly rejected! It displays "Tool Execution Denied: Tool call not allowed due to policy enforcement [Policy evaluation denied due to no determining policies]"!
The policy error for tool calls returns in this format:
{
"jsonrpc": "2.0",
"id": 2,
"result": {
"content": [
{
"type": "text",
"text": "AuthorizeActionException - Tool Execution Denied: Tool call not allowed due to policy enforcement [Policy evaluation denied due to no determining policies]"
}
],
"isError": true
}
}
Conclusion
In this article, I tried out the newly preview-released Policy feature!
I'd like to test it with my own Gateway and tools to verify it works properly.
I'll continue to share my testing results on this blog!
I hope this article was helpful! Thank you for reading!!

