This page covers Amazon Bedrock — the managed model API service that provides access to foundation models from Amazon, Anthropic, Meta, Mistral, and others via a unified inference API. In scope: Bedrock managed model API, Bedrock Guardrails, Bedrock Agents, and Bedrock Knowledge Bases. Not in scope: Amazon SageMaker training and self-hosted models (these have separate hardening surfaces not covered here).
For the underlying threat model and cross-cutting GenAI security principles that apply to all managed LLM API services, see General GenAI Hardening. Key infrastructure prerequisites are covered on sibling pages: aws-iam-08 — SCP deny-list at Organizations (SCP pattern used in multiple controls on this page) and AWS Network (VPC endpoint pattern for aws-genai-03).
Controls are ordered severity-descending: three CRITICAL PREVENTIVE controls appear first (IAM least privilege, prompt attack detection, Agent role scoping), followed by six HIGH controls, then one MEDIUM control. Equivalence links to Azure OpenAI, GCP Vertex AI, and OCI Generative AI controls are HTML comments during authoring and will be made live in the Phase 14 Wave 4 seal.
Scope bedrock:InvokeModel and bedrock:InvokeModelWithResponseStream IAM permissions to specific model ARNs (e.g., arn:aws:bedrock:us-east-1::foundation-model/anthropic.claude-3-5-sonnet-20241022-v2:0). Never grant bedrock:InvokeModel with resource *. Use condition keys bedrock:ModelId and bedrock:InferenceProfileArn to restrict callers to approved model versions. Attach policies to roles, not IAM users. Wildcard resource grants allow a compromised role to pivot to any model — including expensive frontier models with larger capability profiles than the intended use case.
Remediation — AWS CLI
# AWS CLI v2 — enumerate available foundation models
aws bedrock list-foundation-models \
--output json \
--query 'modelSummaries[].{id:modelId,provider:providerName,status:modelLifecycle.status}'
# Simulate effective permissions for a role
aws iam simulate-principal-policy \
--policy-source-arn "${ROLE_ARN}" \
--action-names bedrock:InvokeModel \
--resource-arns "arn:aws:bedrock:us-east-1::foundation-model/anthropic.claude-3-5-sonnet-20241022-v2:0" \
--query 'EvaluationResults[].{action:EvalActionName,decision:EvalDecision}'
AWSTemplateFormatVersion: '2010-09-09'
Description: Least-privilege IAM role allowing a single application to invoke a single Bedrock model.
Parameters:
AppPrincipalArn:
Type: String
AllowedModelId:
Type: String
Default: anthropic.claude-3-5-sonnet-20241022-v2:0
Resources:
BedrockInvokerRole:
Type: AWS::IAM::Role
Properties:
RoleName: bedrock-app-invoker
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
AWS: !Ref AppPrincipalArn
Action: sts:AssumeRole
ManagedPolicyArns:
- !Ref BedrockInvokeOnlyPolicy
BedrockInvokeOnlyPolicy:
Type: AWS::IAM::ManagedPolicy
Properties:
ManagedPolicyName: bedrock-invoke-one-model
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- bedrock:InvokeModel
- bedrock:InvokeModelWithResponseStream
Resource: !Sub 'arn:aws:bedrock:${AWS::Region}::foundation-model/${AllowedModelId}'
Remediation — AWS CDK (TypeScript)
import * as cdk from 'aws-cdk-lib';
import { aws_iam as iam } from 'aws-cdk-lib';
import { Construct } from 'constructs';
export interface BedrockInvokerProps extends cdk.StackProps {
appPrincipalArn: string;
allowedModelId: string;
}
export class BedrockInvokerStack extends cdk.Stack {
constructor(scope: Construct, id: string, props: BedrockInvokerProps) {
super(scope, id, props);
const policy = new iam.ManagedPolicy(this, 'BedrockInvokeOneModel', {
managedPolicyName: 'bedrock-invoke-one-model',
statements: [
new iam.PolicyStatement({
actions: ['bedrock:InvokeModel', 'bedrock:InvokeModelWithResponseStream'],
resources: [`arn:aws:bedrock:${this.region}::foundation-model/${props.allowedModelId}`],
}),
],
});
new iam.Role(this, 'BedrockInvokerRole', {
roleName: 'bedrock-app-invoker',
assumedBy: new iam.ArnPrincipal(props.appPrincipalArn),
managedPolicies: [policy],
});
}
}
Compliance mapping
CIS AWS Foundations v3.0.0
CIS Microsoft Azure Foundations v3.0.0
CIS GCP Foundation v4.0.0
CIS OCI Foundation v2.0.0
NIST SP 800-53 rev5
ISO/IEC 27001:2022
ISO/IEC 27017:2015
OWASP LLM Top 10:2025
NIST AI 600-1 (Jul 2024)
EU AI Act (2024/1689)
n/a (no dedicated CIS GenAI benchmark)
n/a (no dedicated CIS GenAI benchmark)
n/a (no dedicated CIS GenAI benchmark)
n/a (no dedicated CIS GenAI benchmark)
AC-2; AC-6; IA-2
A.5.15; A.5.18
CLD.12.1.5
LLM06:2025; LLM08:2025
Information Security
Art. 55 (in force 2025-08-02)
Log signals
CloudTrail iam:AttachUserPolicy or iam:AttachRolePolicy events granting bedrock-runtime:* or bedrock:* to principals outside the documented AI-platform allow-list — opens the foundation-model invocation surface to non-AI workloads or human users, defeating the IAM-scoping intent.
CloudTrail bedrock-runtime:InvokeModel events whose userIdentity.arn matches a principal not enumerated in the canonical allow-list TSV — the SDK call path indicates the over-permissioned principal is actually exercising the model invocation surface, which is the actionable second-order signal.
CloudTrail iam:CreateRole followed within minutes by iam:PutRolePolicy with a permission statement including bedrock-runtime:InvokeModel on Resource:"*" — the role-creation pattern frequently used to grant a one-off principal access to Bedrock without going through the platform team.
Query
fields @timestamp, eventName, requestParameters.userName, requestParameters.roleName, requestParameters.policyArn, requestParameters.policyDocument, userIdentity.arn
| filter eventSource = "iam.amazonaws.com" and eventName in ["AttachUserPolicy","AttachRolePolicy","PutRolePolicy","PutUserPolicy"]
| filter @message like /bedrock-runtime/ or @message like /"bedrock:\*"/ or @message like /BedrockFullAccess/
| sort @timestamp desc
| limit 50
The CloudWatch Logs Insights filter pattern-matches on the bedrock IAM action namespace; pair with a daily Lambda that walks every principal with bedrock-runtime permissions via iam:SimulatePrincipalPolicy and diffs the resulting set against the AI-platform allow-list TSV maintained in the IaC repository.
Alert threshold
Any principal acquiring bedrock-runtime:* or BedrockFullAccess outside the AI-platform allow-list — page immediately; the bedrock-runtime surface includes high-cost model invocations and the principal can incur material spend before detection if alerting lags.
An InvokeModel call from a principal outside the allow-list — high-priority ticket within 15 minutes; the over-permissioned principal is actively using the privilege rather than just holding it.
Role-create-then-policy-attach sequence (within 5 minutes of role-create) granting bedrock permissions — page; the rapid sequence is a deliberate side-step of the platform team's review process.
Initial response
Detach the over-privileged policy with aws iam detach-role-policy or detach-user-policy; if the principal was created specifically for the over-privileged access, delete the role / user after archiving its CloudTrail trail for forensic review.
Inventory the principal's InvokeModel calls during the over-privileged window via CloudTrail; enumerate model IDs, regions, and request sizes to scope the cost-impact and the data-leakage surface (inputText payloads frequently contain sensitive prompt content).
Open an incident via general/ir.html if the inputText payloads contained customer data or proprietary content; the model provider's invocation log retains the prompts for the foundation-model provider's diagnostic retention window and may require a customer-data-deletion request to be filed.
Enable Bedrock Guardrails prompt attack detection policy. Prompt attack detection requires the Standard tier of Bedrock Guardrails. Basic tier does not include this capability. Verify your guardrail tier in the Bedrock console before relying on this control. Standard tier applies a separate ML classifier to detect jailbreak attempts, prompt injection, and prompt leakage before the prompt reaches the foundation model. This is DISTINCT from the content filter policy (aws-genai-02): prompt attack detection is an input-layer classification that identifies adversarial instructions attempting to override the system prompt, whereas the content filter moderates harm categories in outputs. Both must be configured — neither alone is sufficient.
Remediation — AWS CLI
# AWS CLI v2 — check if prompt attack detection is configured on a guardrail
aws bedrock get-guardrail \
--guardrail-id "${GUARDRAIL_ID}" \
--query "promptAttackConfig"
# Create a guardrail with prompt attack detection enabled (Standard tier)
# The tier must be STANDARD for prompt attack detection to be available
aws bedrock create-guardrail \
--name "prod-guardrail" \
--blocked-input-messaging "This input has been flagged and cannot be processed." \
--blocked-outputs-messaging "This output has been filtered." \
--prompt-attack-filter-strength HIGH
Remediation — Terraform
# Terraform AWS provider ~> 5.0
# Note: prompt attack detection requires Bedrock Guardrails Standard tier.
# Verify the tier argument name against the Terraform Registry for hashicorp/aws
# at the version pinned in your configuration (~> 5.60.0 minimum recommended).
resource "aws_bedrock_guardrail" "prompt_attack" {
name = var.guardrail_name
blocked_input_messaging = "This input has been flagged and cannot be processed."
blocked_outputs_messaging = "This output has been filtered."
prompt_attack_policy_config {
filters_config {
type = "PROMPT_ATTACK"
input_strength = "HIGH"
}
}
}
Remediation — CloudFormation
AWSTemplateFormatVersion: '2010-09-09'
Description: Bedrock Guardrail with HIGH-strength PROMPT_ATTACK filter and minimal content baseline.
Resources:
PromptAttackGuardrail:
Type: AWS::Bedrock::Guardrail
Properties:
Name: prompt-attack-guardrail
BlockedInputMessaging: Your input was blocked because it appears to attempt prompt injection.
BlockedOutputsMessaging: The model response was blocked due to a guardrail violation.
ContentPolicyConfig:
FiltersConfig:
- Type: PROMPT_ATTACK
InputStrength: HIGH
OutputStrength: NONE
Remediation — AWS CDK (TypeScript)
import * as cdk from 'aws-cdk-lib';
import { aws_bedrock as bedrock } from 'aws-cdk-lib';
import { Construct } from 'constructs';
export class PromptAttackGuardrailStack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
new bedrock.CfnGuardrail(this, 'PromptAttackGuardrail', {
name: 'prompt-attack-guardrail',
blockedInputMessaging: 'Your input was blocked because it appears to attempt prompt injection.',
blockedOutputsMessaging: 'The model response was blocked due to a guardrail violation.',
contentPolicyConfig: {
filtersConfig: [
{ type: 'PROMPT_ATTACK', inputStrength: 'HIGH', outputStrength: 'NONE' },
],
},
});
}
}
Compliance mapping
CIS AWS Foundations v3.0.0
CIS Microsoft Azure Foundations v3.0.0
CIS GCP Foundation v4.0.0
CIS OCI Foundation v2.0.0
NIST SP 800-53 rev5
ISO/IEC 27001:2022
ISO/IEC 27017:2015
OWASP LLM Top 10:2025
NIST AI 600-1 (Jul 2024)
EU AI Act (2024/1689)
n/a (no dedicated CIS GenAI benchmark)
n/a (no dedicated CIS GenAI benchmark)
n/a (no dedicated CIS GenAI benchmark)
n/a (no dedicated CIS GenAI benchmark)
SI-10; SI-15
A.8.28
n/a
LLM01:2025
Information Integrity
Art. 55 (in force 2025-08-02)
Log signals
CloudTrail bedrock:DeleteGuardrail events on production guardrails whose name matches the canonical naming convention — destroys the prompt-attack filter outright; subsequent invocations route directly to the foundation model without input filtering.
CloudTrail bedrock:UpdateGuardrail events where the diff removes a contentPolicyConfig.filtersConfig entry of type PROMPT_ATTACK, or where strength is downgraded from HIGH to NONE / LOW — silently weakens the prompt-attack filter while the guardrail remains in place.
Bedrock invocation logs (CloudWatch Logs group configured via aws-genai-04-invocation-logging) where the amazon-bedrock-guardrails-trace.assessments[*].topicPolicy.topics[*].action shifts from BLOCKED to NONE at the rate level — passive signal that the guardrail is being bypassed at invocation time even when the configuration looks intact.
Query
fields @timestamp, eventName, requestParameters.guardrailIdentifier, requestParameters.name, requestParameters.contentPolicyConfig, userIdentity.arn
| filter eventSource = "bedrock.amazonaws.com" and eventName in ["DeleteGuardrail","UpdateGuardrail","CreateGuardrail"]
| filter @message like /PROMPT_ATTACK/ or eventName = "DeleteGuardrail"
| sort @timestamp desc
| limit 50
The CloudWatch Logs Insights filter targets prompt-attack-relevant guardrail mutations; downstream the invocation-time signal can be queried against the Bedrock invocation log group using the structured amazon-bedrock-guardrails-trace JSON.
Alert threshold
Any DeleteGuardrail on a production guardrail — page immediately; downstream applications using the guardrail in their invocation parameters will fail-open to unfiltered model responses at the moment of delete.
Prompt-attack filter strength downgrade — high-priority ticket within 15 minutes; the downgrade may be a deliberate tuning step but should always trace to an experiment-tracking ticket with a documented test result.
Invocation-trace BLOCKED-to-NONE rate shift above 10% week-over-week — informational; promote to incident if the shift exceeds 50% since it indicates either a configuration regression or an attacker successfully tuning their prompts to evade the filter.
Initial response
Restore the guardrail from IaC with aws bedrock update-guardrail or create-guardrail referencing the canonical configuration; confirm via get-guardrail that the PROMPT_ATTACK filter is present with the expected strength.
Audit the Bedrock invocation logs during the gap window for any prompt patterns matching known prompt-injection / jailbreak idioms (instruction-override, role-impersonation, system-prompt-leakage); these prompts were unfiltered and the corresponding model outputs may contain leaked instructions or sensitive content.
Open an incident via general/ir.html if any unfiltered invocation produced a response that the guardrail would have blocked; the downstream consumer of the response may have acted on adversary-controlled content and the consumer's recent actions need a manual review.
Scope Bedrock Agent action-group execution roles to the minimum required ARNs — no wildcard * in Resource for Lambda function invocations, S3 objects, or API Gateway endpoints. Each action group should have its own least-privileged IAM role. Bedrock Agents inherit the execution role's permissions during orchestration; an over-permissioned role enables an adversary who can inject into the agent's reasoning trace to trigger unintended tool calls across the full scope of the wildcard permission. See aws-iam-07 — permission boundaries for the permission boundary pattern that provides an additional containment layer for delegated roles.
Remediation — AWS CLI
# AWS CLI v2 — inspect Bedrock Agent action group configuration
aws bedrock-agent get-agent-action-group \
--agent-id "${AGENT_ID}" \
--agent-version "DRAFT" \
--action-group-id "${AG_ID}" \
--query "{name:actionGroupName,executor:actionGroupExecutor,state:actionGroupState}"
# Verify the execution role trust policy restricts to Bedrock Agent service principal
aws iam get-role \
--role-name "${AGENT_EXECUTION_ROLE_NAME}" \
--query "Role.AssumeRolePolicyDocument"
import * as cdk from 'aws-cdk-lib';
import { aws_iam as iam } from 'aws-cdk-lib';
import { Construct } from 'constructs';
export interface AgentRoleProps extends cdk.StackProps {
agentName: string;
allowedModelId: string;
}
export class AgentExecutionRoleStack extends cdk.Stack {
constructor(scope: Construct, id: string, props: AgentRoleProps) {
super(scope, id, props);
const policy = new iam.ManagedPolicy(this, 'AgentExecPolicy', {
managedPolicyName: `bedrock-agent-${props.agentName}-exec-policy`,
statements: [
new iam.PolicyStatement({
sid: 'InvokeOneModel',
actions: ['bedrock:InvokeModel'],
resources: [`arn:aws:bedrock:${this.region}::foundation-model/${props.allowedModelId}`],
}),
],
});
new iam.Role(this, 'AgentExecutionRole', {
roleName: `bedrock-agent-${props.agentName}-exec`,
assumedBy: new iam.ServicePrincipal('bedrock.amazonaws.com', {
conditions: {
StringEquals: { 'aws:SourceAccount': this.account },
ArnLike: { 'aws:SourceArn': `arn:aws:bedrock:${this.region}:${this.account}:agent/*` },
},
}),
managedPolicies: [policy],
});
}
}
Compliance mapping
CIS AWS Foundations v3.0.0
CIS Microsoft Azure Foundations v3.0.0
CIS GCP Foundation v4.0.0
CIS OCI Foundation v2.0.0
NIST SP 800-53 rev5
ISO/IEC 27001:2022
ISO/IEC 27017:2015
OWASP LLM Top 10:2025
NIST AI 600-1 (Jul 2024)
EU AI Act (2024/1689)
n/a (no dedicated CIS GenAI benchmark)
n/a (no dedicated CIS GenAI benchmark)
n/a (no dedicated CIS GenAI benchmark)
n/a (no dedicated CIS GenAI benchmark)
AC-2; AC-6; AC-17
A.5.15; A.5.18
n/a
LLM06:2025; LLM08:2025
Information Security
Art. 55 (in force 2025-08-02)
Log signals
CloudTrail bedrock-agent:UpdateAgent or bedrock-agent:CreateAgent events whose requestParameters.agentResourceRoleArn resolves to a role with effective policies including iam:*, sts:AssumeRole on broad principals, or any *FullAccess managed policy.
CloudTrail bedrock-agent:UpdateActionGroup events introducing a new actionGroupExecutor.lambda Lambda ARN whose function role has not been pre-approved for agent-tool execution — the action-group Lambda is the agent's tool-use surface and its IAM blast radius bounds the agent's effective capabilities.
CloudWatch Logs entries from the agent's invocation log group where the agent invoked a Lambda action-group function and the function returned an unexpected resource interaction (e.g. modified an unrelated S3 bucket, attempted KMS decrypt outside its policy) — operational signal that an agent has been tricked into using its tools for unauthorized actions.
The CloudWatch Logs Insights query catches the agent-config mutation events; pair with a Lambda that on a daily cadence simulates each agent's execution role via iam:SimulatePrincipalPolicy against the documented agent-tool action set and reports any deviation from the expected minimal action surface.
Alert threshold
Any agent created or updated with an execution role outside the agent-execution-role allow-list — page immediately; the agent's autonomous tool-use blast radius is bounded by the role's policy graph and broad permissions there are equivalent to giving an LLM admin keys.
Action-group Lambda introduced with an unapproved IAM role — high-priority ticket within 30 minutes; the action-group function is the agent's call-out point and any new function should go through the platform team's tool-onboarding review.
Agent invocation log entry showing unexpected resource interaction — page; this is the canonical signal of a prompt-injection attack that successfully redirected the agent's tool use toward an attacker-chosen target.
Initial response
Pause the agent immediately via aws bedrock-agent update-agent setting agentResourceRoleArn to a minimal-deny stub role; this stops the agent from executing any further tool calls without removing the agent's invocation surface (so consumers see explicit denials rather than silent gaps).
Restore the canonical execution role from IaC and re-validate the agent's action-group Lambda ARNs against the allow-list; if any action-group Lambda was modified during the gap window, revert it from source control and re-deploy.
Open an incident via general/ir.html; pull the agent's invocation logs for the gap window and enumerate every tool call that resulted in a resource mutation — each mutation needs a manual review against the prompt that initiated it to confirm whether the action was the user's intent or an attacker's prompt-injection payload.
Configure Bedrock Guardrails content filter policy with explicit harm-category thresholds for HATE, SEXUAL, VIOLENCE, INSULTS, MISCONDUCT, and PROMPT_ATTACK set to HIGH or MEDIUM for both input and output. Configure PII redaction using sensitive_information_policy_config with pii_entities_config per entity type (EMAIL → ANONYMIZE; AWS_ACCESS_KEY → BLOCK). This is a separate Guardrails configuration block from prompt attack detection (aws-genai-06): the content filter policy moderates harm categories in prompts and completions, whereas prompt attack detection is an input-layer classifier for adversarial injection patterns.
Anti-pattern: Setting any harm category filter strength to NONE is equivalent to the BLOCK_NONE anti-pattern documented in General GenAI — Common Misconfigurations. Any policy deviation must be risk-accepted with compensating controls documented. All harm categories should be set to HIGH or at minimum MEDIUM for both input and output.
Audit — AWS CLI
# AWS CLI v2 — check content filter policy thresholds on a guardrail
aws bedrock get-guardrail \
--guardrail-id "${GUARDRAIL_ID}" \
--output json | jq '.contentPolicy'
# List all guardrails in the account to verify coverage
aws bedrock list-guardrails \
--output json \
--query 'guardrails[].{id:guardrailId,name:name,version:version,status:status}'
Remediation — Terraform
# Terraform AWS provider ~> 5.0 (min 5.60.0 for pii_entities_config input_action/output_action)
resource "aws_bedrock_guardrail" "this" {
name = var.guardrail_name
blocked_input_messaging = "I cannot process that input."
blocked_outputs_messaging = "I cannot provide that output."
sensitive_information_policy_config {
pii_entities_config {
type = "EMAIL"
input_action = "ANONYMIZE"
output_action = "ANONYMIZE"
}
pii_entities_config {
type = "AWS_ACCESS_KEY"
input_action = "BLOCK"
output_action = "BLOCK"
}
pii_entities_config {
type = "NAME"
input_action = "ANONYMIZE"
output_action = "ANONYMIZE"
}
}
content_policy_config {
filters_config {
type = "HATE"
input_strength = "HIGH"
output_strength = "HIGH"
}
filters_config {
type = "SEXUAL"
input_strength = "HIGH"
output_strength = "HIGH"
}
filters_config {
type = "VIOLENCE"
input_strength = "HIGH"
output_strength = "HIGH"
}
filters_config {
type = "INSULTS"
input_strength = "MEDIUM"
output_strength = "HIGH"
}
filters_config {
type = "MISCONDUCT"
input_strength = "MEDIUM"
output_strength = "HIGH"
}
}
}
Remediation — CloudFormation
AWSTemplateFormatVersion: '2010-09-09'
Description: Bedrock Guardrail enforcing HIGH-strength content filters on hate/insults/sexual/violence/misconduct.
Resources:
ContentFilterGuardrail:
Type: AWS::Bedrock::Guardrail
Properties:
Name: content-filter-guardrail
BlockedInputMessaging: Your message was blocked by a content filter.
BlockedOutputsMessaging: The model response was blocked by a content filter.
ContentPolicyConfig:
FiltersConfig:
- Type: HATE
InputStrength: HIGH
OutputStrength: HIGH
- Type: INSULTS
InputStrength: HIGH
OutputStrength: HIGH
- Type: SEXUAL
InputStrength: HIGH
OutputStrength: HIGH
- Type: VIOLENCE
InputStrength: HIGH
OutputStrength: HIGH
- Type: MISCONDUCT
InputStrength: HIGH
OutputStrength: HIGH
Compliance mapping
CIS AWS Foundations v3.0.0
CIS Microsoft Azure Foundations v3.0.0
CIS GCP Foundation v4.0.0
CIS OCI Foundation v2.0.0
NIST SP 800-53 rev5
ISO/IEC 27001:2022
ISO/IEC 27017:2015
OWASP LLM Top 10:2025
NIST AI 600-1 (Jul 2024)
EU AI Act (2024/1689)
n/a (no dedicated CIS GenAI benchmark)
n/a (no dedicated CIS GenAI benchmark)
n/a (no dedicated CIS GenAI benchmark)
n/a (no dedicated CIS GenAI benchmark)
SI-15; SC-28
A.8.28; A.5.34
CLD.6.3.1
LLM01:2025; LLM02:2025
Dangerous/Violent Content; Data Privacy
Art. 55 (in force 2025-08-02)
Log signals
CloudTrail bedrock:UpdateGuardrail events removing or downgrading any of the standard content-filter categories (VIOLENCE, HATE, SEXUAL, INSULTS, MISCONDUCT) — drops the topical-output filter and exposes downstream consumers to unfiltered model responses in those categories.
CloudTrail bedrock:DeleteGuardrailVersion events on a published version still referenced by production agents or applications — leaves applications pointing at a now-missing version, which fails-open in some SDK error paths.
Bedrock invocation logs (the CloudWatch Logs group from aws-genai-04) where the guardrail trace shows category-level action=NONE on responses that previously returned action=BLOCKED for similar inputs — passive operational signal of filter weakening.
Query
fields @timestamp, eventName, requestParameters.guardrailIdentifier, requestParameters.contentPolicyConfig.filtersConfig, requestParameters.guardrailVersion, userIdentity.arn
| filter eventSource = "bedrock.amazonaws.com" and eventName in ["UpdateGuardrail","DeleteGuardrailVersion","CreateGuardrailVersion"]
| filter @message like /VIOLENCE/ or @message like /HATE/ or @message like /SEXUAL/ or @message like /MISCONDUCT/
| sort @timestamp desc
| limit 50
The CloudWatch Logs Insights filter pattern-matches on the content-filter category names; the regex over the JSON payload is faster than a typed filter because filtersConfig is a list whose category strings are easier to surface via raw-message matching.
Alert threshold
Any content-filter category downgrade to NONE in production — page immediately; the corresponding downstream consumer (chatbot, agent, RAG application) is now exposed to unfiltered output in that category which may violate the org's acceptable-use policy.
DeleteGuardrailVersion on a version still referenced by production traffic — page; the version deletion is irreversible and applications referencing it will fail or fail-open until they are reconfigured to point at a newer version.
Invocation-trace action=BLOCKED rate dropping below the 7-day baseline by more than 50% — informational; the drop may indicate either upstream input changes or filter weakening, and the operational team should investigate which before escalating.
Initial response
Restore the content-filter configuration from IaC with aws bedrock update-guardrail referencing the canonical filtersConfig; create a new published version and update production applications to reference it before deleting the un-canonical interim version.
For invocation-trace shifts, sample a stratified set of responses from the post-shift window and run them through the canonical guardrail in evaluation mode (aws bedrock-runtime apply-guardrail) to identify which responses would now be flagged — these are the candidate consumer-exposed unsafe responses.
Open an incident via general/ir.html for the consumer-exposure case; the responses that the weaker guardrail let through may have been shown to end users and the resulting consumer-facing content needs a manual review against the org's content policy.
Create VPC Interface Endpoints for com.amazonaws.<region>.bedrock-runtime (inference) and com.amazonaws.<region>.bedrock (control plane) to route inference traffic through the AWS backbone, never traversing the public internet. Deny public Bedrock invocations via SCP using the aws:SourceVpc condition key to enforce that all bedrock:InvokeModel calls originate from within an approved VPC. Without this control, prompts, completions, and system prompts traverse corporate egress infrastructure where they may be logged, inspected, or intercepted. See aws-net-05 — VPC endpoints / PrivateLink for the general VPC endpoint pattern.
Remediation — AWS CLI
# AWS CLI v2 — check for existing Bedrock VPC endpoints
aws ec2 describe-vpc-endpoints \
--filters "Name=service-name,Values=com.amazonaws.${REGION}.bedrock-runtime" \
"Name=vpc-endpoint-type,Values=Interface" \
--query 'VpcEndpoints[].{id:VpcEndpointId,vpc:VpcId,state:State,dns:DnsEntries[0].DnsName}'
# Verify both service names are available in the target region
aws ec2 describe-vpc-endpoint-services \
--service-names \
"com.amazonaws.${REGION}.bedrock-runtime" \
"com.amazonaws.${REGION}.bedrock" \
--query 'ServiceDetails[].{name:ServiceName,available:ServiceType[0].ServiceType}'
CloudTrail ec2:DeleteVpcEndpoints events targeting com.amazonaws.{region}.bedrock-runtime or com.amazonaws.{region}.bedrock interface endpoints — removes the private-network path so Bedrock API calls fall back to the public endpoint via NAT, exposing prompt traffic to potential SSL-interception or DNS-hijack attacks.
CloudTrail ec2:ModifyVpcEndpoint where the endpoint policy is widened to allow cross-account principals without an organisation-condition — opens the endpoint's invocation surface to external accounts which can then route Bedrock calls through the org's VPC.
VPC Flow Logs from private subnets to the public Bedrock-runtime IP ranges (resolvable from the AWS IP-range JSON for service BEDROCK_RUNTIME) — direct evidence that traffic is taking the public path despite the endpoint being configured.
Query
fields @timestamp, eventName, requestParameters.vpcEndpointId, requestParameters.serviceName, requestParameters.policyDocument, userIdentity.arn
| filter eventSource = "ec2.amazonaws.com" and eventName in ["DeleteVpcEndpoints","ModifyVpcEndpoint","CreateVpcEndpoint"]
| filter @message like /bedrock-runtime/ or @message like /com.amazonaws.[a-z0-9-]+\.bedrock/
| sort @timestamp desc
| limit 50
The CloudWatch Logs Insights filter scopes to bedrock endpoint mutations; pair with a daily completeness check that lists describe-vpc-endpoints per region and confirms the bedrock-runtime endpoint is present in every region the org operates AI workloads.
Alert threshold
Any DeleteVpcEndpoints on a bedrock-runtime endpoint in production — page immediately; downstream Bedrock SDK calls fall back to the public endpoint via NAT within DNS TTL and the network-isolation posture evaporates.
Endpoint-policy widening introducing a non-condition-bound principal — high-priority ticket within 15 minutes; the legitimate cross-account use case for Bedrock endpoints is rare and the policy widening should map to a documented platform-team decision.
Private-subnet flows to bedrock public IP ranges above 100 flows per hour — high-priority ticket; the workload is bypassing the endpoint either because its SDK lacks endpoint-discovery or because a route-table change has diverted traffic to NAT.
Initial response
Re-create the bedrock-runtime endpoint from IaC with aws ec2 create-vpc-endpoint --service-name com.amazonaws.{region}.bedrock-runtime --vpc-id {vpc} --policy-document file://canonical-policy.json --subnet-ids {subnets} --security-group-ids {sgs}; verify SDK calls now resolve to the endpoint's private DNS name via a smoke-test from a workload pod.
For endpoint-policy widening, restore the IaC-canonical policy via modify-vpc-endpoint and enumerate any cross-account principal that issued calls against the endpoint during the permissive window from the consumer-account CloudTrail.
Open an incident via general/ir.html if the gap window included sensitive prompt traffic (PII, intellectual property, customer data); the public-path fallback exposes prompt and response payloads to the broader public network surface and the data-classification owner must be notified for a confidentiality-impact assessment.
Enable Bedrock model invocation logging to CloudWatch Logs and/or S3 with KMS-CMK encryption. Configure minimum 90-day log retention. Invocation logging captures the full request metadata (model ID, input tokens, output tokens, latency) and optionally the prompt and completion text. Log the text content for audit purposes but apply PII redaction before storage — the Guardrails PII redaction layer (aws-genai-02) operates at inference time and is complementary to, not a replacement for, PII-aware log storage configuration. Use aws bedrock put-model-invocation-logging-configuration to enable; invocation logging is disabled by default and requires explicit activation per region.
CloudTrail bedrock:PutModelInvocationLoggingConfiguration events where the new requestParameters.loggingConfig.cloudWatchConfig is null, or where imageDataDeliveryEnabled / textDataDeliveryEnabled / embeddingDataDeliveryEnabled are set to false — disables the invocation log payload delivery without removing the configuration entirely.
CloudTrail bedrock:DeleteModelInvocationLoggingConfiguration events — destroys the invocation-logging configuration at the account level; subsequent InvokeModel calls do not land in the log group and the audit / forensic surface goes dark.
Absence-of-signal: the configured Bedrock invocation log group's ingestion rate (per-hour bytes) drops to zero or below 5% of trailing-7-day baseline while InvokeModel CloudTrail events continue to flow at normal rate — indicates the logging pipeline is broken even if the configuration looks correct.
The CloudWatch Logs Insights query catches the configuration mutations; pair with a CloudWatch alarm on the invocation log group's IncomingBytes metric with an absence-of-signal threshold at 5% of the trailing 7-day rolling mean.
Alert threshold
Any DeleteModelInvocationLoggingConfiguration in production — page immediately; the audit-and-cost-tracking visibility for foundation-model use vanishes at the moment of the delete.
cloudWatchConfig=null or any *DataDeliveryEnabled=false on a production account — page within 5 minutes; partial-logging is worse than no-logging from an audit standpoint because it gives false confidence in coverage.
Log group ingestion below 5% of baseline for 30 minutes while InvokeModel events continue — high-priority ticket; the pipeline is broken end-to-end and needs platform-team investigation.
Initial response
Restore the logging configuration with aws bedrock put-model-invocation-logging-configuration --logging-config file://canonical-logging.json referencing the canonical CloudWatch log-group ARN and all delivery flags enabled.
For ingestion-gap cases, verify the logging-delivery IAM role's policy still grants logs:CreateLogStream + logs:PutLogEvents on the destination log group; the most common silent-failure mode is a role-policy drift that breaks delivery without surfacing as a Bedrock-side error.
Open an incident via general/ir.html if any sensitive prompts or responses were exchanged during the logging-off window; the gap-of-coverage means the audit-trail evidence required by some compliance regimes (HIPAA, financial-services AI use) is missing for those invocations.
Enable CloudTrail data event recording for AWS::Bedrock::InvokeModel and AWS::Bedrock::InvokeAgent event types. Data events are not captured by default in CloudTrail management events — explicit configuration via put-event-selectors or advanced event selectors is required. CloudTrail data events provide API-level caller attribution (IAM principal ARN, source IP, user agent) for every model invocation, enabling forensic reconstruction of which identity invoked which model at what time. This control is complementary to invocation logging (aws-genai-04): data events provide control-plane caller attribution; invocation logging provides data-plane prompt and completion capture.
CloudTrail PutEventSelectors events on the Bedrock-data-events trail where the advancedEventSelectors array removes the AWS::Bedrock::AgentAlias or AWS::Bedrock::KnowledgeBase data-resource selectors — drops object-level visibility on agent and knowledge-base interactions, leaving only management-event coverage.
CloudTrail PutEventSelectors events where the bedrock-data-events trail's selector list is replaced with an empty array — disables data-event capture entirely while leaving the trail in place.
Volume signal: the Bedrock-data-event ingestion rate in the dedicated trail's S3 destination drops to zero while CloudTrail management events for Bedrock continue at normal rate — indicates the data-event side of the trail is silently broken even if the configuration looks correct.
Query
fields @timestamp, eventName, requestParameters.trailName, requestParameters.advancedEventSelectors, requestParameters.eventSelectors, userIdentity.arn
| filter eventSource = "cloudtrail.amazonaws.com" and eventName = "PutEventSelectors"
| filter requestParameters.trailName like /-bedrock-/ or requestParameters.trailName like /-genai-/
| sort @timestamp desc
| limit 50
The CloudWatch Logs Insights filter targets bedrock-specific data-event trails by name pattern; the trail-name allow-list should be maintained as an SSM Parameter Store value so the filter does not drift as new trails are added.
Alert threshold
Any PutEventSelectors on the bedrock-data-events trail reducing resource coverage — page immediately; the data-event capture is the foundational forensic surface for AI workload activity and reductions narrow the investigation window.
Empty advancedEventSelectors on a bedrock-data-events trail — page within 5 minutes; the trail is now a management-only trail in functional terms even though the name still suggests data-event coverage.
Bedrock-data-event ingestion below 5% of trailing-7-day baseline for 30 minutes while management events continue — high-priority ticket; the data-event pipeline is silently broken and the platform team must investigate the delivery role / destination configuration.
Initial response
Restore the advanced-event-selectors from IaC with aws cloudtrail put-event-selectors --trail-name {arn} --advanced-event-selectors file://canonical-bedrock-selectors.json; confirm via get-event-selectors read-back that the canonical resource-type list is restored.
For ingestion gaps, validate the trail's S3 destination bucket policy still grants s3:PutObject to the CloudTrail service principal with the trail's source-arn condition; the most common silent-failure mode is bucket-policy drift after a different team modified the bucket.
Open an incident via general/ir.html if the gap window included any agent-execution or knowledge-base mutation activity; the data-event record is the canonical evidence for those operations and the absence creates a forensic blind spot for any subsequent incident touching the affected agent / knowledge base.
Secure Bedrock Knowledge Base data sources by: (1) enforcing VPC-only S3 bucket policies that deny non-VPC access using the aws:SourceVpc condition key; (2) configuring per-source ingestion-time PII filtering to redact sensitive data before it enters the vector store; (3) scoping the Bedrock Knowledge Base service role to only the specific S3 bucket prefix containing the knowledge base data, not the entire bucket. Prevent RAG poisoning by ensuring only authorized, change-controlled sources can be ingested into the vector store — an attacker with write access to the S3 source can inject malicious instructions that are embedded and retrieved on every subsequent RAG query.
CloudTrail bedrock:UpdateKnowledgeBase or bedrock-agent:UpdateKnowledgeBase events where the roleArn is changed to a role with effective policies broader than the data-source's minimal-read permission set — opens the knowledge-base ingestion path to data the consumer is not supposed to access.
CloudTrail bedrock:CreateDataSource or UpdateDataSource events where the dataSourceConfiguration.s3.bucketArn or ...inclusionPrefixes widens to include data outside the documented knowledge-base scope — silently expands the corpus the agent / retrieval flow can answer from.
Knowledge-base retrieval logs (when invocation logging from aws-genai-04 is on) showing retrieved snippets whose source object is outside the documented data-source-prefix set — the operational signal that an over-broad data-source is actively contributing to model responses.
Query
fields @timestamp, eventName, requestParameters.knowledgeBaseId, requestParameters.roleArn, requestParameters.dataSourceConfiguration, userIdentity.arn
| filter eventSource in ["bedrock.amazonaws.com","bedrock-agent.amazonaws.com"] and eventName in ["UpdateKnowledgeBase","CreateKnowledgeBase","UpdateDataSource","CreateDataSource"]
| sort @timestamp desc
| limit 50
The CloudWatch Logs Insights query covers both the role-arn and data-source-configuration mutation paths; pair with a Lambda that on a 5-minute cadence diffs the current data-source configuration against the IaC-canonical YAML and alarms on any deviation from the documented prefix and bucket allow-list.
Alert threshold
Knowledge-base role-arn change to a role outside the per-knowledge-base allow-list — page immediately; the new role's policy graph determines which data the knowledge base ingests and broad permissions expose the knowledge base to data classifications it was not designed to handle.
Data-source bucket or prefix widening — page within 5 minutes; the knowledge base will re-index the newly-in-scope data on its next sync cycle and the new content becomes retrievable by every agent / application calling Retrieve.
Retrieved snippet sourced from outside the documented data-source set — high-priority ticket; the operator must verify whether the source is legitimately in scope (data-source-configuration drift) or whether an attacker has poisoned the knowledge-base store with malicious objects.
Initial response
Revert the knowledge-base configuration from IaC with aws bedrock-agent update-knowledge-base and update-data-source; trigger a re-ingestion via start-ingestion-job so the corpus state aligns with the restored configuration.
Identify any retrieval-response from the gap window that included content from the over-broad data source by querying the invocation logs; flag the consumer applications that received those responses for a follow-up review on whether the responses leaked content the consumer was not authorized to see.
Open an incident via general/ir.html if the over-broad data source contained customer data, intellectual property, or regulated content; the data-owner must be notified per the data-classification tag and a content-removal flow may be required across consumer-side caches and chat-history logs.
Enforce Bedrock Guardrails at the AWS Organizations level by: (1) requiring Guardrail association on all InvokeModel calls via SCP using the bedrock:GuardrailIdentifier condition key; (2) denying bedrock:CreateGuardrail without required cost-allocation and environment tags; (3) using AWS Organizations delegated admin for Bedrock to centralize Guardrail management across member accounts. This is the cross-account enforcement layer that prevents member accounts from invoking models without a guardrail policy — per-account Guardrails must exist before this SCP can be applied. See aws-iam-08 — SCP deny-list for the general SCP authoring pattern.
Audit — AWS CLI
# AWS CLI v2 — list SCPs applied in the organization
aws organizations list-policies \
--filter SERVICE_CONTROL_POLICY \
--query 'Policies[].{id:Id,name:Name,awsManaged:AwsManaged}'
# Enumerate guardrails in the current account
aws bedrock list-guardrails \
--output json \
--query 'guardrails[].{id:guardrailId,name:name,status:status}'
CloudTrail organizations:UpdatePolicy events on the SCP that mandates guardrail attachment, where the diff removes the Deny statement on bedrock:InvokeModel without a guardrailIdentifier condition — drops the org-level enforcement so member accounts can invoke models without a guardrail.
CloudTrail organizations:DetachPolicy events removing the guardrail-enforcement SCP from any OU or member account — narrows the enforcement scope without touching the policy itself.
CloudTrail bedrock-runtime:InvokeModel events whose requestParameters.guardrailIdentifier is absent in member accounts post-detach — confirms operational regression rather than just configuration drift.
Query
fields @timestamp, eventName, requestParameters.policyId, requestParameters.targetId, requestParameters.content, userIdentity.arn
| filter eventSource = "organizations.amazonaws.com" and eventName in ["UpdatePolicy","DetachPolicy","DeletePolicy","AttachPolicy"]
| filter @message like /bedrock/ or @message like /InvokeModel/
| sort @timestamp desc
| limit 50
Run the CloudWatch Logs Insights query against the management-account trail; Organizations events route there and the management-account is the canonical source of truth for org-level SCP state.
Alert threshold
Any UpdatePolicy on the guardrail-enforcement SCP that weakens the Deny — page immediately and engage the org-security owner; the SCP is the fleet-wide guarantee that no Bedrock invocation lands without a guardrail.
DetachPolicy removing the SCP from a production OU — page within 10 minutes; the OU's member accounts can now invoke Bedrock without guardrails until the policy is re-attached.
InvokeModel calls without guardrailIdentifier from accounts in the previously-protected OU — page; the operational signal confirms member-account workloads are exercising the regression.
Initial response
Restore the SCP from IaC with aws organizations update-policy --policy-id {id} --content file://canonical-scp.json; re-attach to any OU detached during the gap via attach-policy.
Inventory InvokeModel calls without guardrailIdentifier during the gap window across all member accounts and identify the responsible workloads; pause those workloads' Bedrock IAM permissions temporarily so they cannot continue invocations until they are reconfigured to pass the guardrail.
Open an incident via general/ir.html if any consumer-facing responses were generated without guardrail enforcement during the gap window; the responses bypassed the org's content-policy controls and may have surfaced unsafe or policy-violating output to end users.
Lock cross-region inference to approved geographic profiles by: (1) restricting which inference profiles are callable using the bedrock:InferenceProfileRegion IAM condition key in policies and SCPs; (2) denying bedrock:GetInferenceProfile on profile ARNs not in an explicit allow-list; (3) documenting the approved provisioned-throughput regions in your data-residency policy. AWS cross-region inference can automatically route requests to different AWS regions for capacity — without explicit allow-listing this routing can violate data-residency requirements by sending prompts and completions to unapproved geographic locations. This is especially relevant for GDPR, DPDPA, and similar data localisation regulations where prompt data constitutes personal data.
Audit — AWS CLI
# AWS CLI v2 — enumerate available system-defined cross-region inference profiles
aws bedrock list-inference-profiles \
--type-equals SYSTEM_DEFINED \
--query 'inferenceProfileSummaries[].{id:inferenceProfileId,name:inferenceProfileName,regions:models[].modelArn}'
# Inspect routing regions for a specific inference profile
aws bedrock get-inference-profile \
--inference-profile-identifier "${PROFILE_ARN}" \
--query "{id:inferenceProfileId,status:status,models:models}"
CloudTrail bedrock-runtime:InvokeModel events whose requestParameters.modelId resolves to a cross-region inference profile (ID prefix us., eu., apac.) in an account whose data-residency policy mandates single-region invocation — defeats the residency posture by routing prompt traffic across regional boundaries.
CloudTrail bedrock:CreateInferenceProfile events creating a cross-region profile in an account or OU that the org policy restricts to single-region — the creation precedes the abuse and is the early-warning signal.
SCP-deny events in CloudTrail (eventName ending in InvokeModel with errorCode=AccessDenied and errorMessage referencing the cross-region SCP) — informational, indicates the preventive guard fired but also indicates the workload is attempting cross-region invocations that need to be redirected.
Query
fields @timestamp, eventName, requestParameters.modelId, requestParameters.inferenceProfileId, awsRegion, errorCode, userIdentity.arn
| filter eventSource in ["bedrock-runtime.amazonaws.com","bedrock.amazonaws.com"] and eventName in ["InvokeModel","Converse","CreateInferenceProfile","DeleteInferenceProfile"]
| filter requestParameters.modelId like /^us\./ or requestParameters.modelId like /^eu\./ or requestParameters.modelId like /^apac\./ or requestParameters.inferenceProfileId like /cross-region/
| sort @timestamp desc
| limit 100
The CloudWatch Logs Insights filter pattern-matches against the cross-region inference-profile ID prefixes; the prefix is the canonical signal because Bedrock encodes the routing geography in the model-ID itself.
Alert threshold
Any InvokeModel against a cross-region profile from an account whose data-residency policy bans cross-region inference — page immediately; the prompt and response payloads may cross jurisdictional boundaries and trigger regulatory disclosure obligations.
CreateInferenceProfile for a cross-region profile in a restricted account — page within 15 minutes; the creation alone does not violate residency but creates the surface for subsequent violation and the org policy mandates the creation itself be blocked.
SCP-deny events targeting cross-region invocations above 10 per hour — informational; the preventive is firing as designed but the workload owners need to be redirected to the regional inference profile rather than continuing to retry against the cross-region one.
Initial response
If the SCP that bans cross-region invocations is missing, restore it from IaC via aws organizations update-policy + attach-policy; the SCP is the preventive layer and its presence is what makes the InvokeModel calls deny rather than succeed.
For invocations that already succeeded against a cross-region profile, identify the prompt and response payloads via the Bedrock invocation log group and document them as a residency-impact event; consult Legal / Compliance for jurisdictional notification requirements.
Open an incident via general/ir.html if the workload handles personal data subject to residency law (EU GDPR data subjects, certain US state laws, financial-services jurisdictional restrictions); the data-subject impact and notification timeline are bounded by the cross-region exposure window and the response must include both the configuration remediation and a regulatory-impact assessment.