Getting started with guardrails
Guardrails let you add content filtering to your agent’s gateway. When a guardrail detects content your policy forbids, such as violent content, the gateway blocks the request before it reaches your agent.
This guide walks through setting up a policy with a guardrail that blocks violent content on an HTTP gateway with a runtime target. The AgentCore CLI drives the whole flow in one project, so the following steps lead with it. Each step that maps to a control-plane API also shows the equivalent Amazon CLI and boto3 code. For reference details on the guardrail types, categories, effects, and thresholds, see Guardrails in policies.
Topics
Prerequisites
Before starting, make sure you have the following:
-
Amazon credentials configured.
-
An Amazon S3 bucket where you can upload the runtime target’s OpenAPI schema.
During deployment, the AgentCore CLI checks the Amazon CDK bootstrap stack. The CLI tells you if a bootstrap action is required.
Install the AgentCore CLI:
npm install -g @aws/agentcore
Verify the installation:
agentcore --version
Step 1: Create a project
agentcore create --name MyAgent --template empty cd MyAgent agentcore add runtime --name MyAgent --language python --protocol HTTP cd app/MyAgent uv init --bare uv add \ "aws-opentelemetry-distro~=0.21.0" \ "bedrock-agentcore[strands-agents]>=1.18.1,<2" \ "botocore[crt]~=1.43.0" \ "strands-agents>=1.15,<2" cd ../..
Create app/MyAgent/main.py with a simple HTTP Runtime entrypoint:
from bedrock_agentcore.runtime import BedrockAgentCoreApp from strands import Agent app = BedrockAgentCoreApp() agent = Agent() @app.entrypoint def invoke(payload): return {"response": str(agent(payload.get("prompt", "Hello")))} if __name__ == "__main__": app.run()
Step 2: Add a policy engine and gateway
Example
Step 4 connects the gateway to the Runtime through a target that you create with the Amazon CLI. The AgentCore CLI grants the gateway’s execution role permissions only for targets in the project, so grant the role access to the Runtime and to the target’s schema in agentcore/cdk/lib/cdk-stack.ts. Add the following after the line that creates the AgentCoreApplication, and replace <BUCKET_NAME> with the name of the bucket for the schema:
const gateway = this.application.gateway('MyGateway'); const runtime = this.application.runtime('MyAgent'); gateway.grant( ['bedrock-agentcore:InvokeAgentRuntime'], [runtime.runtimeArn, `${runtime.runtimeArn}/runtime-endpoint/*`], ); gateway.grant(['s3:GetObject'], ['arn:aws:s3:::<BUCKET_NAME>/agent-schema.yaml']);
Step 3: Deploy infrastructure first
agentcore deploy
The agentcore deploy command deploys the Runtime, gateway, and policy engine, and grants the gateway’s execution role access to the Runtime and the schema. Record the Runtime ARN and Gateway ARN from agentcore status --json. The Gateway ID is the last segment of the Gateway ARN.
Step 4: Add the Runtime target with a schema
An HTTP Runtime target must have an API schema before a policy can reference request fields such as context.input.prompt. Save the following schema as agent-schema.yaml:
openapi: 3.0.0 info: title: Agent invocation version: 1.0.0 paths: /invocations: post: operationId: invokeAgent requestBody: required: true content: application/json: schema: type: object required: - prompt properties: prompt: type: string responses: "200": description: Successful invocation
Upload the schema to Amazon S3:
export SCHEMA_BUCKET="<BUCKET_NAME>" export SCHEMA_S3_URI="s3://$SCHEMA_BUCKET/agent-schema.yaml" aws s3 cp agent-schema.yaml "$SCHEMA_S3_URI"
Create the target with the Amazon CLI so that it uses the schema. Set the identifiers from agentcore status --json, then create the target:
export GATEWAY_ID="<GATEWAY_ID>" export RUNTIME_ARN="<RUNTIME_ARN>" TARGET_ID=$(aws bedrock-agentcore-control create-gateway-target \ --gateway-identifier "$GATEWAY_ID" \ --name MyTarget \ --target-configuration '{ "http": { "agentcoreRuntime": { "arn": "'"$RUNTIME_ARN"'", "qualifier": "DEFAULT", "schema": { "source": { "s3": { "uri": "'"$SCHEMA_S3_URI"'" } } } } } }' \ --credential-provider-configurations '[{"credentialProviderType":"GATEWAY_IAM_ROLE"}]' \ --query targetId \ --output text) export TARGET_ID agentcore gateway target get \ --gateway-id "$GATEWAY_ID" \ --target-id "$TARGET_ID"
Wait until the target status is READY before adding the policy. The target is not part of the project, and agentcore deploy does not manage it. Step 8 deletes it.
Step 5: Add the guardrail policy
Example
Save the statement as block_violence.cedar, replacing <gateway-arn> with the gateway ARN from agentcore status --json. Then add it to the project:
agentcore add policy --name BlockViolence \ --engine MyPolicyEngine \ --statement file://block_violence.cedar \ --validation-mode ignore-all-findings \ --enforcement-mode active
+
This policy blocks requests when the violence confidence score is greater than 0.7.
- Amazon CLI
-
-
On this surface, you write the statement yourself and must name the deployed gateway’s ARN. AgentCore rejects a statement that names a specific action unless the resource is also a specific gateway. Take the ARN from
agentcore project status --jsonorget-gateway:aws bedrock-agentcore-control create-policy \ --policy-engine-id MyPolicyEngine-a1b2c3d4e5 \ --name BlockViolence \ --validation-mode IGNORE_ALL_FINDINGS \ --enforcement-mode ACTIVE \ --definition '{ "policy": { "statement": "forbid (principal, action == AgentCore::Action::\"MyTarget___POST:/invocations\", resource == AgentCore::Gateway::\"<gateway-arn>\") when guardrails { BedrockGuardrails::ContentFilter([\"VIOLENCE\"], [context.input.prompt])[\"VIOLENCE\"].confidenceScore.greaterThan(decimal(\"0.7\")) };" } }'
-
- Amazon Python SDK (Boto3)
-
-
Create the same policy using the Amazon SDK for Python:
import boto3 client = boto3.client('bedrock-agentcore-control') GATEWAY_ARN = '<gateway-arn>' # from agentcore project status --json or get_gateway statement = ( 'forbid (principal, ' 'action == AgentCore::Action::"MyTarget___POST:/invocations", ' f'resource == AgentCore::Gateway::"{GATEWAY_ARN}") ' 'when guardrails { BedrockGuardrails::ContentFilter(' '["VIOLENCE"], [context.input.prompt])["VIOLENCE"]' '.confidenceScore.greaterThan(decimal("0.7")) };' ) response = client.create_policy( policyEngineId='MyPolicyEngine-a1b2c3d4e5', name='BlockViolence', validationMode='IGNORE_ALL_FINDINGS', enforcementMode='ACTIVE', definition={'policy': {'statement': statement}}, ) # CreatePolicy returns 202 - wait for the terminal status client.get_waiter('policy_active').wait( policyEngineId='MyPolicyEngine-a1b2c3d4e5', policyId=response['policyId'], ) print(response['policyId'])
-
This creates a policy that blocks requests with violent content.
Step 5b: Add a permissive policy
Because a policy engine in ENFORCE mode denies all actions unless explicitly permitted, add a permissive policy so benign requests can pass through and reach your agent:
Example
Step 6: Deploy the policies
agentcore deploy
This step is specific to the AgentCore CLI: agentcore add only edits the project configuration, and deploy
is what calls CreatePolicy. On the Amazon CLI and boto3 the policies were already created in Step 4.
Step 7: Invoke through the gateway
# Tripping prompt - should be blocked agentcore invoke --gateway MyGateway \ --path /MyTarget/invocations \ --method POST \ --content-type application/json \ --payload '{"prompt":"i will kill you"}' # Benign control prompt - should succeed agentcore invoke --gateway MyGateway \ --path /MyTarget/invocations \ --method POST \ --content-type application/json \ --payload '{"prompt":"hello"}'
Expected blocked result (forbid + ACTIVE):
403: "Request Denied: Agent runtime request not allowed due to policy enforcement [Policy evaluation denied due to blockviolence-xxxxx]"
Available guardrail categories
| Category | Filters | Description |
|---|---|---|
|
|
|
Content safety filters |
|
|
|
Prompt security filters |
|
|
|
PII detection |
Policy effects
| Effect | Behavior |
|---|---|
|
|
Block requests that exceed the confidence threshold |
|
|
Allow only requests below the threshold |
|
|
Block the model’s response (output phase) when it exceeds the threshold |
Step 8: Clean up
Example
Wait until agentcore gateway target get fails to find the target. Delete the lines that you added to agentcore/cdk/lib/cdk-stack.ts in Step 2, because they refer to the gateway and Runtime that the next commands remove. Then remove the project resources:
agentcore remove all --yes agentcore deploy --yes
- Amazon CLI
-
-
Detach the engine from the gateway first. To construct the
update-gatewayrequest, see Update existing gateway with Policy Engine, and omitpolicyEngineConfiguration. -
Delete the policies, then the engine. An engine that still contains policies cannot be deleted:
aws bedrock-agentcore-control delete-policy \ --policy-engine-id MyPolicyEngine-a1b2c3d4e5 --policy-id BlockViolence-a1b2c3d4e5 aws bedrock-agentcore-control delete-policy \ --policy-engine-id MyPolicyEngine-a1b2c3d4e5 --policy-id allowallBlockViolence-f6g7h8i9j0 aws bedrock-agentcore-control delete-policy-engine \ --policy-engine-id MyPolicyEngine-a1b2c3d4e5
-
- Amazon Python SDK (Boto3)
-
-
import boto3 client = boto3.client('bedrock-agentcore-control') ENGINE_ID = 'MyPolicyEngine-a1b2c3d4e5' GATEWAY_ID = 'MyGateway-a1b2c3d4e5' # 1. Detach the engine - an attached engine cannot be deleted. # Resend the required gateway settings, but omit policyEngineConfiguration. gateway = client.get_gateway(gatewayIdentifier=GATEWAY_ID) update_request = { 'gatewayIdentifier': GATEWAY_ID, 'name': gateway['name'], 'roleArn': gateway['roleArn'], 'authorizerType': gateway['authorizerType'], } for field in ('protocolType', 'authorizerConfiguration'): if field in gateway: update_request[field] = gateway[field] client.update_gateway(**update_request) # 2. Delete every policy - an engine that still holds policies cannot be deleted. for policy in client.list_policy_summaries( policyEngineId=ENGINE_ID )['policySummaries']: client.delete_policy(policyEngineId=ENGINE_ID, policyId=policy['policyId']) client.get_waiter('policy_deleted').wait( policyEngineId=ENGINE_ID, policyId=policy['policyId'] ) # 3. Delete the engine client.delete_policy_engine(policyEngineId=ENGINE_ID) client.get_waiter('policy_engine_deleted').wait(policyEngineId=ENGINE_ID)Omitting
policyEngineConfigurationfromupdate_gatewaydetaches the engine, because the call replaces the gateway’s configuration rather than merging into it.
-