Subchapter 15.1
references/policy.mdMarkdown10 KBView on GitHub
Control what your AgentCore agent can do — restrict tool calls, enforce business rules, and protect sensitive operations.
$ARGUMENTS is optional:
/policy # interactive — asks what you want to restrict
/policy generate # generate Cedar from natural language
/policy debug # diagnose why a policy is allowing/denying
/policy emergency # generate an emergency shutdown policyAgentCore Policy enforces Cedar-based authorization rules at the gateway boundary — before any tool call reaches its target. Every tool call is evaluated against your policies in real time.
Default behavior: Without a policy engine attached to your gateway, all tool calls are allowed. Once you attach a policy engine, the default is deny — you must write explicit permit policies for everything you want to allow.
Key concepts:
forbid overrides permit — if any forbid policy matches, the action is denied regardless of permit policiesRead agentcore/agentcore.json to understand:
agentCoreGateways array)policyEngines array)Ask (or infer from $ARGUMENTS):
“What do you want to control?
- Restrict a tool based on input values (e.g., amount < $500)
- Role-based access (only certain users can call certain tools)
- Block a specific tool entirely
- Emergency shutdown — disable all tools immediately
- Debug why a policy is allowing or denying unexpectedly”
# Create and attach to an existing gateway
agentcore add policy-engine \
--name MyPolicyEngine \
--attach-to-gateways MyGateway \
--attach-mode LOG_ONLYStart with LOG_ONLY mode — policies are evaluated and logged but not enforced. This lets you verify your policies work correctly before enabling enforcement.
Switch to ENFORCE when ready:
# Update an existing gateway
agentcore add gateway \
--name MyGateway \
--policy-engine MyPolicyEngine \
--policy-engine-mode ENFORCE(The same --policy-engine and --policy-engine-mode flags work at gateway creation time too.)
agentcore deploy -y[!WARNING] Cedar policies that reference a specific gateway ARN in the
resourcefield require the gateway to be deployed first. You cannot add a policy with a gateway ARN before the gateway exists in AWS.Two-phase deployment:
- Deploy the gateway first:
agentcore deploy -y- Get the gateway ARN:
agentcore status --type gateway --json- Add the policy with the real ARN, then deploy again
The
-g/--generateflag also requires a deployed gateway — it calls an AWS API that needs the gateway ARN to convert natural language into Cedar. If you run-gbefore deploying the gateway, it will fail.
Requires the gateway to be deployed first — the CLI calls an API that needs the gateway ARN.
# Deploy the gateway first
agentcore deploy -y
# Then generate the policy (--gateway tells the CLI which deployed gateway to use)
agentcore add policy \
--name refund_policy \
--engine MyPolicyEngine \
-g "Allow users with the refund-agent role to process refunds when the amount is less than 500" \
--gateway MyGatewayThe CLI generates Cedar from your description, resolves the gateway ARN automatically, and validates the result. Review the generated policy before deploying.
Policy name rules: letters, numbers, underscores only — no hyphens. refund-policy fails; refund_policy works.
Save to a .cedar file and register. If the policy references a gateway ARN in the resource field, you need the ARN from a prior deploy:
# Get the gateway ARN after deploying
agentcore status --type gateway --json | jq -r '.gateways[0].arn'
# Update your .cedar file with the real ARN, then add the policy
agentcore add policy \
--name refund_policy \
--engine MyPolicyEngine \
--source policy.cedarAction name format: AgentCore::Action::"TargetName___tool_name" — three underscores between target name and tool name. This is the most common Cedar mistake.
// TargetName is the gateway target name (from agentcore add gateway-target --name)
// tool_name is the tool name within that target
AgentCore::Action::"RefundTarget___process_refund"
// ^^^
// three underscoresPrincipal types:
AgentCore::OAuthUser — authenticated user via OAuth/JWTAgentCore::IamEntity — IAM-authenticated caller (when gateway uses AWS_IAM auth). The id attribute contains the full IAM ARN.Resource format:
AgentCore::Gateway::"arn:aws:bedrock-agentcore:<REGION>:<YOUR_ACCOUNT_ID>:gateway/<GATEWAY_ID>"Get your gateway ARN: agentcore status --type gateway --json | jq -r '.gateways[0].arn'
Amount-based restriction:
permit(
principal is AgentCore::OAuthUser,
action == AgentCore::Action::"RefundTarget___process_refund",
resource == AgentCore::Gateway::"arn:aws:bedrock-agentcore:us-east-1:123456789012:gateway/my-gateway-id"
)
when {
principal.hasTag("role") &&
principal.getTag("role") == "refund-agent" &&
context.input.amount < 500
};Role-based access (OAuth user):
permit(
principal is AgentCore::OAuthUser,
action == AgentCore::Action::"AdminTarget___delete_record",
resource == AgentCore::Gateway::"arn:..."
)
when {
principal.hasTag("role") &&
["admin", "superuser"].contains(principal.getTag("role"))
};Account-based access (IAM entity):
permit(
principal is AgentCore::IamEntity,
action == AgentCore::Action::"AdminTarget___delete_record",
resource == AgentCore::Gateway::"arn:..."
)
when {
principal.id like "arn:aws:iam::123456789012:*"
};Block a specific tool entirely:
forbid(
principal,
action == AgentCore::Action::"PaymentTarget___transfer_funds",
resource == AgentCore::Gateway::"arn:..."
);Emergency shutdown — disable all tools:
forbid(principal, action, resource);Required field validation:
forbid(
principal is AgentCore::OAuthUser,
action == AgentCore::Action::"InsuranceTarget___file_claim",
resource == AgentCore::Gateway::"arn:..."
)
unless {
context.input has description &&
context.input has priority
};Always use hasTag() before getTag():
// ❌ Wrong — throws error if tag doesn't exist
when { principal.getTag("role") == "admin" }
// ✅ Correct — check existence first
when {
principal.hasTag("role") &&
principal.getTag("role") == "admin"
}Default deny: Once a policy engine is attached in ENFORCE mode, everything is denied unless a permit policy matches. Write explicit permits for every action you want to allow.
forbid always wins: A forbid policy overrides any permit policy. Use this for emergency shutdowns and hard blocks.
In LOG_ONLY mode, all requests are allowed but policy decisions are logged to CloudWatch. Use this to verify your policies before switching to ENFORCE.
# Check policy decision logs
agentcore logs --runtime MyAgent --since 1h --query "policy"Look for log entries showing ALLOW or DENY decisions for each tool call.
agentcore add policy \
--name test_policy \
--engine MyPolicyEngine \
--source policy.cedar \
--validation-mode FAIL_ON_ANY_FINDINGSIf the Cedar syntax is invalid, the CLI returns a validation error before creating the policy.
Once LOG_ONLY results look correct:
# Update gateway to enforce mode
agentcore add gateway \
--name MyGateway \
--policy-engine MyPolicyEngine \
--policy-engine-mode ENFORCE
agentcore deploy -y“Access denied” on a tool call you expect to allow:
permit policy exists for this action — remember, default is denyTargetName___tool_name (three underscores)hasTag() is used before getTag() in conditions# Check recent policy decisions
agentcore logs --runtime MyAgent --since 1h --query "policy"
agentcore status --type policy-engine“Everything is being denied” after attaching a policy engine:
You attached a policy engine but haven’t written any permit policies yet. The default is deny. Write at least one permit policy for the actions you want to allow.
Policy name validation error:
Policy names must match ^[A-Za-z][A-Za-z0-9_]*$ — letters, numbers, underscores only, starts with a letter. No hyphens.