Amazon Bedrock Security and Governance - IAM Condition Keys, SCP Design, PrivateLink, and Multi-Account Guardrail Enforcement

First Published:
Last Updated:

Rolling out Amazon Bedrock across an organization is rarely blocked by "can we call a model." The hard part is the opposite: making sure that every team can only call approved models, always through a required guardrail, only over a private network path, with every invocation captured for audit — and doing all of that consistently across dozens of AWS accounts without hand-editing each one.

This article is a Level 400 walkthrough of a single, named reference architecture for exactly that problem: the Multi-Account Bedrock Governance Foundation. It follows one model invocation through every control point — Service Control Policy (SCP), IAM identity policy, VPC endpoint policy, guardrail, and the two audit systems (model invocation logging and CloudTrail) — and then shows how to distribute those controls to every account with CloudFormation StackSets and validate them in CI/CD with CloudFormation Guard.

The scope here is Bedrock's own multi-account governance: IAM, SCP, private connectivity, audit, and at-scale distribution. Adjacent deep dives are delegated rather than repeated, so this article stays on the governance plane and links out instead of duplicating.

One honesty note up front, because this is a security topic: passing every control in this article does not mean "safe." These controls raise the floor and shrink the blast radius, but they are one layer. Guardrails can be bypassed on the input side under specific configurations, logs can be misconfigured, and some principals are simply out of scope for SCPs. Defense is layered, and the last section is explicit about where each control stops working. Every IAM action name, condition key, service name, and logging behavior below was verified against the current AWS documentation; anything not confirmable is called out rather than invented.

1. Introduction

Generative AI governance inverts the usual platform question. For most services, the platform team's job is to make a capability available. For foundation models, availability is trivial — a single bedrock:InvokeModel call reaches a model — and the platform team's job becomes constraint: which models, under which guardrail, from which network, logged where.

Four questions define an organization-wide Bedrock posture:
  • Which models can be invoked? Enforced with SCPs (organization-wide) and IAM identity policies (per-account), scoped by foundation-model ARN.
  • Is a guardrail always applied? Enforced with the bedrock:GuardrailIdentifier IAM condition key.
  • Is the traffic private? Enforced with interface VPC endpoints (AWS PrivateLink) and endpoint policies.
  • Is every invocation auditable? Split across two systems — model invocation logging (the prompt and completion bodies) and CloudTrail (the invocation metadata) — which record fundamentally different things.

The rest of this article builds one architecture that answers all four, traces a request through it, gives the reproducible policy for each control point, and then covers the failure modes that appear when these controls interact — most notably the collision between region-scoped SCPs and cross-region inference. The threat model that motivates layering these controls is covered in the defense-in-depth model for AI agents, and the guardrail application architecture in Responsible AI Guardrails Architecture on AWS.

Not in scope (delegated):
  • Guardrail policy types, detection modes, and the ApplyGuardrail API — delegated to the Bedrock Guardrails implementation deep dive.
  • PII detection and redaction with Amazon Comprehend, Bedrock Guardrails, and Amazon Macie — delegated to the PII patterns article.
  • Fine-grained (column-level) access to AI data in the lake — delegated to the AWS Lake Formation article.
  • AgentCore identity, token vaults, and Cedar authorization — delegated to the AgentCore security guide.
  • Bedrock terminology — see the Amazon Bedrock Glossary.

2. The Reference Architecture at a Glance

The Multi-Account Bedrock Governance Foundation is built from eight AWS capabilities working together. It assumes an AWS Organizations layout with a management account, one or more organizational units (OUs) holding workload accounts, and a dedicated log-archive account — the standard multi-account baseline.
Multi-Account Bedrock Governance Foundation reference architecture
Multi-Account Bedrock Governance Foundation reference architecture
The eight components and their governance role:
  • AWS Organizations and SCPs — the organization-wide guardrail. SCPs cap what any principal in a workload account can do: which model vendors are allowed, and which Regions Bedrock may run in. SCPs are boundaries, not grants.
  • AWS Organizations Bedrock policies — the organization-wide safeguard enforcement. A dedicated Organizations policy type that automatically enforces safeguards configured in Bedrock Guardrails across the organization, an OU, or an account — more centralized than making a guardrail mandatory role by role with IAM condition keys, and the two approaches compose.
  • IAM identity policies and ABAC — the per-account grant. Roles are granted bedrock:InvokeModel on specific approved model ARNs, with a required guardrail expressed as a condition, and attribute-based access control (ABAC) applied where the resource type supports tags.
  • Interface VPC endpoints (AWS PrivateLink) and endpoint policies — the network boundary. Bedrock runtime traffic stays on the private network, and the endpoint policy is a second place to restrict which models and actions are reachable.
  • Model invocation logging — the content audit. When enabled, it captures the actual prompt and completion bodies per invocation.
  • CloudTrail (organization trail) — the activity audit. It records who called which Bedrock API, when, and from where — as metadata, not content.
  • CloudFormation StackSets — the distribution mechanism. It pushes the guardrail, logging, and IAM baselines to every account and auto-deploys to accounts newly added to an OU.
  • CloudFormation Guard — the CI/CD gate. It validates the templates for policy compliance (guardrail present, logging enabled) before they are deployed.

The distinguishing property of this architecture is that no single control is trusted alone. A model invocation must satisfy the SCP and the IAM policy and the endpoint policy, then run through the guardrail, then be recorded by the audit systems. The next section follows exactly that path.

Each control point answers a different question, is evaluated at a different moment, and fails with a different symptom. The table below is the map the rest of the article fills in.

* You can sort the table by clicking on the column name.
ControlScopeWhat it restrictsWhere it is evaluatedTypical failure symptom
SCPOrganization / OUModel vendors, RegionsBefore IAM, on every request in the accountBroad AccessDenied across the account or Region
IAM identity policyPer roleModel ARN, required guardrail, ABAC on taggable resourcesDuring IAM authorizationAccessDenied for one role or one model
VPC endpoint policyPer endpointAction, principal, model ARN over that endpointAt the interface endpointDenied only when traffic uses that endpoint
Guardrail (GuardrailIdentifier)Per requestPresence of a specific guardrailIAM condition, then guardrail applicationDenied when the guardrail is missing or wrong
Model invocation loggingAccount and RegionNothing — it records contentAfter invocationMissing prompt/completion content
CloudTrailAccount / organizationNothing — it records metadataAfter invocationMetadata present, but no bodies (by design)
StackSetsOrganization / OUNothing — it distributes the baselineAt deploy timeAn account drifts from the baseline
CloudFormation GuardCI/CD pipelineNon-compliant templatesBefore deploymentA non-compliant baseline reaches an account

3. How a Model Invocation Is Authorized

This is the core of the architecture: one InvokeModel (or Converse) request from an application role in a workload account, traced through every control point until it is recorded.
Authorization walkthrough for a single Bedrock model invocation
Authorization walkthrough for a single Bedrock model invocation
The request passes through the following gates, in order. A Deny at any gate stops the call.
  1. SCP evaluation (organization boundary). Before IAM even considers the identity policy, the SCPs attached to the account's OU and the account itself are evaluated. If an SCP denies the model vendor or the Region, the call fails here with AccessDenied regardless of what the identity policy allows. SCPs can only remove permissions; they never grant.
  2. IAM identity-based evaluation (per-account grant). The role's identity policy must explicitly allow bedrock:InvokeModel (or bedrock:InvokeModelWithResponseStream) on the target model's ARN. If the policy also requires a guardrail via bedrock:GuardrailIdentifier, a request that omits the guardrail — or names a different one — is denied here.
  3. VPC endpoint policy (network boundary). If the traffic enters Bedrock through an interface VPC endpoint, the endpoint policy is evaluated as a resource-based policy on the endpoint. It can independently restrict the action, the principal, and the model ARN. A request that clears IAM but hits an endpoint whose policy allows only a narrower model set is denied at the endpoint.
  4. Guardrail application. With authorization satisfied, Bedrock applies the guardrail named in the request. The guardrail evaluates the input and, after the model responds, the output. The guardrail's policy types (content filters, denied topics, word filters, sensitive-information filters, contextual grounding) are the subject of the Guardrails deep dive; from the governance plane, the relevant fact is that the bedrock:GuardrailIdentifier condition made the guardrail mandatory.
  5. Model invocation. The model runs. With cross-region inference profiles, the actual execution Region may differ from the caller's Region — which is precisely where SCP region controls can collide (Section 5).
  6. Audit capture. Two independent systems record the call. Model invocation logging (if enabled in this account and Region) writes the prompt and completion to Amazon S3 and/or Amazon CloudWatch Logs. CloudTrail records the API call as a management event — metadata only, no bodies.

The detailed mechanics of steps 1–3 (how AWS combines SCPs, identity policies, and resource-based policies into a single allow/deny decision) are the IAM policy evaluation logic itself; that walkthrough is delegated to the step-by-step IAM policy evaluation logic article. What matters here is the ordering of independent boundaries: organization → account → network → guardrail → audit, each of which must be designed and each of which can fail independently.

As a concrete trace: a role calls InvokeModel on an approved Anthropic model, through the runtime VPC endpoint, naming the required guardrail. The SCP allows the vendor and Region, so evaluation continues. The identity policy allows the model ARN and its bedrock:GuardrailIdentifier condition matches, so IAM allows. The endpoint policy allows the same model, so the network boundary passes. Bedrock applies the guardrail to the prompt, invokes the model, applies the guardrail to the completion, and returns. Model invocation logging writes the prompt and completion to Amazon S3; CloudTrail records the InvokeModel call with the modelId and caller identity but no bodies. Change any one of those — a non-approved model, a missing guardrail identifier, the wrong endpoint, or a Region the SCP does not allow — and the call stops at the corresponding gate with AccessDenied, traceable through the CloudTrail errorCode.

4. IAM for Bedrock: Actions, Condition Keys, and ABAC

IAM is where "approved models only" and "guardrail always" become concrete. Three facts about Bedrock's IAM surface shape every policy you write.

4.1 Actions: the Converse trap

Bedrock's runtime is invoked through a small set of IAM actions:
  • bedrock:InvokeModel
  • bedrock:InvokeModelWithResponseStream
  • bedrock:ApplyGuardrail
  • bedrock:CreateGuardrail (and the other guardrail control-plane actions)

A critical subtlety: Converse and ConverseStream are not separate IAM actions. The Converse API requires the bedrock:InvokeModel permission, and ConverseStream requires bedrock:InvokeModelWithResponseStream. You cannot allow or deny "Converse" independently — any allow or deny you write for InvokeModel applies equally to callers using the Converse API. Policies that try to gate Converse with a bedrock:Converse action silently match nothing, because that action does not exist. Design your policies around InvokeModel / InvokeModelWithResponseStream and treat Converse as a caller of those.

4.2 Restricting to approved models: use resource ARNs, not a model-ID condition key

The official way to restrict which models a role can invoke is by resource ARN, not by a condition key. Foundation models are addressed as:
arn:aws:bedrock:<region>::foundation-model/<model-id>
Note the empty account field — foundation-model ARNs are account-agnostic and cannot be tagged. Scope the Resource (or NotResource) of your InvokeModel statements to the approved model ARNs.

A correctness warning worth stating plainly: some AWS Prescriptive Guidance examples restrict models with a bedrock:ModelId condition key. That condition key is not in the Amazon Bedrock Service Authorization Reference — the documented Bedrock condition keys are a short list that does not include it. A policy that relies on bedrock:ModelId may not behave as written. Restrict models by ARN, which is the documented and reliable mechanism.

4.3 Making a guardrail mandatory: bedrock:GuardrailIdentifier

To force every model call through a specific guardrail, use the bedrock:GuardrailIdentifier condition key (type: ARN). It applies to InvokeModel and InvokeModelWithResponseStream (and therefore to Converse / ConverseStream). The official pattern pairs a positive allow with a bedrock:ApplyGuardrail grant on the guardrail:
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "InvokeApprovedModelsWithRequiredGuardrail",
      "Effect": "Allow",
      "Action": [
        "bedrock:InvokeModel",
        "bedrock:InvokeModelWithResponseStream"
      ],
      "Resource": [
        "arn:aws:bedrock:us-east-1::foundation-model/anthropic.claude-*",
        "arn:aws:bedrock:us-east-1::foundation-model/amazon.nova-*"
      ],
      "Condition": {
        "StringEquals": {
          "bedrock:GuardrailIdentifier": "arn:aws:bedrock:us-east-1:111122223333:guardrail/abcdefgh1234:2"
        }
      }
    },
    {
      "Sid": "AllowApplyOfTheRequiredGuardrail",
      "Effect": "Allow",
      "Action": "bedrock:ApplyGuardrail",
      "Resource": "arn:aws:bedrock:us-east-1:111122223333:guardrail/abcdefgh1234"
    }
  ]
}
The official guardrail-permissions documentation gives several variants of this shape:
  • StringEquals / StringNotEquals to pin a guardrail and a specific numeric version.
  • The version-less guardrail ARN (.../guardrail/<id>) to pin the DRAFT working version.
  • ArnLike / ArnNotLike with .../guardrail/<id>:* to allow any numeric version of one guardrail, or .../guardrail/<id>* to also include DRAFT.
  • StringEquals with an array to allow several guardrail-and-version combinations.

The model ARNs above deliberately use vendor-and-family wildcards (anthropic.claude-*, amazon.nova-*) rather than a single dated model ID. The exact set of currently available model IDs changes often; pin the precise IDs your organization has approved from the current Bedrock model list rather than copying a dated string.

Limits of guardrail enforcement — state them honestly:
  • Multi-invocation APIs. APIs that internally issue several InvokeModel calls (such as RetrieveAndGenerate and InvokeAgent) do not all carry the same guardrail identifier. If a role holds a guardrail-required InvokeModel permission and is used by one of these orchestrating APIs, some internal calls can be denied. Scope the guardrail-required policy to the roles that make direct model calls, and handle orchestrating APIs separately.
  • Input-side bypass. Guardrail input tags allow selectively skipping the guardrail on the input side of a request; the guardrail is always applied on the output side. "A guardrail is attached" is therefore not the same as "the input was screened." Treat guardrail enforcement as necessary, not sufficient.
  • Cross-account sharing and organization-level enforcement. Guardrails support resource-based policies, so a centrally managed guardrail (and its guardrail profile, the resource used for cross-Region guardrail routing) can be shared to other accounts — for example with aws:PrincipalOrgID or aws:PrincipalOrgPaths conditions for organization- or OU-scoped sharing — and applied cross-account. On top of that, AWS Organizations provides a dedicated Amazon Bedrock policy type that enforces safeguards configured in Bedrock Guardrails automatically across an organization, OU, or account, eliminating the need to configure an individual guardrail in every account. Use the Organizations policy for mandatory organization-wide enforcement, and the bedrock:GuardrailIdentifier IAM condition for per-role granularity — the two approaches compose.

4.4 Inference profiles are a different resource type

Cross-region inference and cost tracking use inference profiles, addressed by a different ARN than foundation models and gated by the bedrock:InferenceProfileArn condition key (type: ARN):
  • arn:aws:bedrock:<region>:<account>:inference-profile/<id>system-defined profiles (used for cross-region routing).
  • arn:aws:bedrock:<region>:<account>:application-inference-profile/<id>application profiles you create for cost and usage attribution.

When a role invokes a model through an inference profile, an InvokeModel policy that only allows the bare foundation-model ARN is insufficient — the profile ARN must also be permitted. This split is the root of the cross-region failure mode in Section 5.

4.5 ABAC: which Bedrock resources can be tagged

Attribute-based access control uses the global condition keys aws:ResourceTag/${TagKey}, aws:RequestTag/${TagKey}, and aws:TagKeys. Bedrock defines no bedrock:-prefixed tag condition key; ABAC uses the global keys. What matters is which resource types support tags:
  • Taggable (ABAC works): application-inference-profile, custom-model, provisioned-model, guardrail, knowledge-base, agent, prompt, flow, evaluation-job, and most account-scoped resources.
  • Not taggable (ABAC does not apply): foundation-model and system-defined inference-profile.

The practical consequence: you cannot ABAC-gate access to a foundation model by tag, because foundation-model ARNs are not taggable. ABAC is effective for application inference profiles, guardrails, custom models, provisioned throughput, and knowledge bases — attach a team or environment tag to those and gate access with aws:ResourceTag/team. For raw foundation-model access, restriction is by ARN (Section 4.2), not by tag.

5. SCP Design for Organization-Wide Control

SCPs are the organization-wide ceiling. They apply to the workload accounts under an OU and cannot be overridden by an account's own IAM policies. Because SCPs now support the full IAM policy language, the recommended shape is one broad Allow (or the implicit FullAWSAccess) plus several targeted Deny statements.

5.1 Restrict to approved model vendors

The cleanest organization-wide model restriction denies every foundation model except approved vendors, by ARN:
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "DenyNonApprovedModelVendors",
      "Effect": "Deny",
      "Action": [
        "bedrock:InvokeModel",
        "bedrock:InvokeModelWithResponseStream"
      ],
      "NotResource": [
        "arn:aws:bedrock:*::foundation-model/amazon.*",
        "arn:aws:bedrock:*::foundation-model/anthropic.*"
      ]
    }
  ]
}
This denies any model whose ARN does not match the approved vendor prefixes, everywhere in the organization. Individual accounts can still narrow further with IAM, but they cannot broaden past this ceiling. AWS's own guidance recommends exactly this "broad allow plus targeted deny" SCP structure for Bedrock model governance.

5.2 Restrict Bedrock to approved Regions

Region control is a global-condition-key deny on aws:RequestedRegion:
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "DenyBedrockOutsideApprovedRegions",
      "Effect": "Deny",
      "Action": "bedrock:*",
      "Resource": "*",
      "Condition": {
        "StringNotEquals": {
          "aws:RequestedRegion": [
            "us-east-1",
            "us-west-2",
            "unspecified"
          ]
        }
      }
    }
  ]
}
AWS Control Tower ships an equivalent OU-level Region deny control (CT.MULTISERVICE.PV.1) if you prefer a managed control over a hand-written SCP. Note the unspecified entry in the allow list — that is not a typo, and Section 5.3 explains why it is mandatory when cross-region inference is in play.

5.3 The cross-region inference collision (the key failure mode)

This is the single most important interaction to design for, and it is explicitly documented by AWS. Cross-region inference (CRIS) lets Bedrock route a request to a model in a different Region than the caller's, for capacity and resilience. Two flavors interact with SCPs differently:
  • Global cross-region inference. A Global CRIS request is evaluated with aws:RequestedRegion equal to the literal string "unspecified". If your region-restriction SCP does not include "unspecified" in its allow list, every Global CRIS request is denied — even from an approved Region. This is why the SCP in Section 5.2 lists unspecified explicitly.
  • Geographic cross-region inference. A Geographic CRIS request requires IAM permissions for three things: the geographic inference-profile ARN, the source Region's foundation-model ARN, and the foundation-model ARN in every destination Region the profile can route to. An SCP or IAM policy that pins the model ARN to a single Region breaks Geographic CRIS the moment Bedrock routes to another Region in the profile.

The design rule that follows: if you use cross-region inference, your model-ARN and Region controls must account for the entire routing surface, not just the caller's Region — allow unspecified for Global CRIS, and allow every destination Region's foundation-model ARN plus the inference-profile ARN for Geographic CRIS. Getting this wrong produces intermittent AccessDenied errors that appear only when traffic happens to route cross-region, which is exactly the diagnostic pattern in Section 9.

The residency and inference-geography design behind this — how to keep a RAG workload contained within a Region while still using inference profiles — is the subject of the Amazon Bedrock Cross-Region Inference and Data Residency Design article: Amazon Bedrock Cross-Region Inference and Data Residency Design.

6. Private Connectivity: VPC Endpoints and Endpoint Policies

Keeping Bedrock traffic off the public internet uses interface VPC endpoints (AWS PrivateLink). Bedrock exposes several endpoint services, each for a different API surface:
  • com.amazonaws.<region>.bedrock — control plane (guardrails, model customization, logging configuration).
  • com.amazonaws.<region>.bedrock-runtime — runtime (InvokeModel, Converse, and streaming variants).
  • com.amazonaws.<region>.bedrock-mantle — the Mantle (Responses) API surface.
  • com.amazonaws.<region>.bedrock-agent — Agents build-time API.
  • com.amazonaws.<region>.bedrock-agent-runtime — Agents runtime API.

FIPS variants (bedrock-fips, bedrock-runtime-fips) exist in a subset of Regions. Note that the newer Amazon Bedrock AgentCore is a separate service with its own VPC/PrivateLink documentation; the bedrock-agent and bedrock-agent-runtime services above are for the original Agents for Amazon Bedrock (now Amazon Bedrock Agents Classic), not AgentCore. Be aware of the lifecycle here: Bedrock Agents Classic closes to new customers on July 30, 2026 and moves to maintenance mode, so treat these two endpoints as governance for existing workloads only — new agent deployments should target AgentCore and its own endpoint set instead.

6.1 Endpoint policies as a second model boundary

Every interface endpoint carries an endpoint policy — a resource-based policy on the endpoint that controls Principal, Action, and Resource. The default policy allows full access; replace it to make the network path itself enforce the model allow-list:
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "AllowApprovedModelsThroughThisEndpointOnly",
      "Effect": "Allow",
      "Principal": "*",
      "Action": [
        "bedrock:InvokeModel",
        "bedrock:InvokeModelWithResponseStream"
      ],
      "Resource": "arn:aws:bedrock:*::foundation-model/anthropic.*"
    }
  ]
}
This is a genuinely independent boundary from IAM: even a role whose identity policy allows a broader model set cannot reach a non-approved model through this endpoint. Endpoint policies can also pin Principal to specific role ARNs when an endpoint is dedicated to one team.

6.2 Forcing traffic through the endpoint

An endpoint policy only helps if callers actually use the endpoint. To require that Bedrock calls arrive via a specific VPC endpoint, add an IAM or resource-policy condition on aws:SourceVpce:
{
  "Sid": "DenyBedrockNotFromApprovedVpce",
  "Effect": "Deny",
  "Action": "bedrock:*",
  "Resource": "*",
  "Condition": {
    "StringNotEquals": {
      "aws:SourceVpce": "vpce-0abc123def456ghij"
    }
  }
}
Combined with private DNS on the endpoint, this makes "Bedrock is only reachable privately, and only for approved models" a property of the network, not just of each role's IAM policy.

7. Audit: Model Invocation Logging and CloudTrail

Auditing Bedrock is where the most common architectural misunderstanding lives: model invocation logging and CloudTrail are not two views of the same data. They record different things, live in different places, and aggregate across accounts differently. You need both, and you need to know which one holds the prompt.

7.1 Model invocation logging: the content record

Model invocation logging captures the actual request and response bodies — prompts, completions, and (optionally) images, embeddings, and video. It is configured with PutModelInvocationLoggingConfiguration (or the Settings page in the Bedrock console), and three properties define its behavior:
  • Scope is per-account, per-Region, and it is disabled by default. There is no single organization-wide switch. Each account, in each Region where Bedrock is used, must be configured individually — which is exactly why StackSets (Section 8) matters.
  • Destinations are S3, CloudWatch Logs, or both. Data-type toggles let you deliver only what you need: textDataDeliveryEnabled, imageDataDeliveryEnabled, embeddingDataDeliveryEnabled, videoDataDeliveryEnabled, audioDataDeliveryEnabled.
  • Large or binary data goes to S3. Bodies at or under 100 KB are inlined into the log record; anything larger, or binary (images), is written to S3 with a reference left in the log. In practice, delivering full bodies for image or large-text workloads requires an S3 destination even if you also use CloudWatch Logs.

A minimal CLI configuration for one account and Region:
aws bedrock put-model-invocation-logging-configuration \
  --region us-east-1 \
  --logging-config '{
    "s3Config": { "bucketName": "org-bedrock-invocation-logs-111122223333" },
    "cloudWatchConfig": {
      "logGroupName": "/aws/bedrock/model-invocations",
      "roleArn": "arn:aws:iam::111122223333:role/BedrockModelInvocationLoggingRole",
      "largeDataDeliveryS3Config": { "bucketName": "org-bedrock-large-data-111122223333" }
    },
    "textDataDeliveryEnabled": true,
    "imageDataDeliveryEnabled": false,
    "embeddingDataDeliveryEnabled": false
  }'
Each log record is a JSON object describing one invocation. Beyond the request and response bodies, a record carries the operation (InvokeModel, Converse, and the streaming variants), the modelId, the accountId and region, a requestId, the caller's identity ARN, and input/output token counts — plus any requestMetadata your application attached to the call. One coverage gap worth noting: invocations made through the Bedrock Mantle (Responses) API surface are not captured by model invocation logging.

The data-protection consequence is the point. By default, Bedrock does not store your prompts and completions; model invocation logging is the moment you choose to persist them. Those logs can contain the pre-guardrail user input — the raw prompt, before any sensitive-information filter ran. You must therefore treat the log bucket and log group as sensitive: encrypt with AWS KMS, restrict access with least privilege (a common pattern separates Basic, SensitiveData, and Admin reader roles), and consider Amazon Macie to detect sensitive data that lands in the logs. Whether to log prompt content at all is a genuine governance decision, not a default to accept — for regulated inputs, you may deliberately disable text delivery and rely on CloudTrail metadata plus your own application-side redaction.

7.2 CloudTrail: the activity record (metadata, not bodies)

CloudTrail records that a Bedrock API was called, by whom, when, and from where — but not the prompt or completion. Two facts about its Bedrock coverage are easy to get wrong:
  • InvokeModel, InvokeModelWithResponseStream, Converse, and ConverseStream are recorded as management events by default — no data-event configuration required. This is unusual; most services treat high-volume runtime calls as data events.
  • A separate set of runtime APIs are data events that require an advanced event selector to capture — including InvokeAgent / InvokeInlineAgent, Retrieve / RetrieveAndGenerate, InvokeFlow, and the bidirectional/async invoke operations. Inference calls made through the Bedrock Mantle (Responses) API surface are likewise recorded as data events (with eventSource bedrock-mantle.amazonaws.com), not as default management events — consistent with Mantle's exclusion from model invocation logging noted in 7.1, its audit trail requires explicit configuration.

Critically, a CloudTrail InvokeModel event carries requestParameters containing only the modelId and similar metadata, with responseElements set to null. There is no prompt and no completion in CloudTrail. If you need the bodies, they are in model invocation logging — not here. This division is deliberate: CloudTrail answers "who invoked what, from where" for security investigation; model invocation logging answers "what was actually sent and returned" for content review.

7.3 Organization-wide aggregation — and what it does not cover

An organization trail (created in the management or a delegated administrator account) automatically captures Bedrock management and data events from every member account into a single S3 bucket, typically in the log-archive account. Member accounts can read but not alter it. This gives you organization-wide metadata aggregation for free.

It does not aggregate model invocation logs. Those are per-account, per-Region, and there is no organization trail equivalent for them. To centralize the prompt/completion bodies you must build it yourself — enable logging in every account (StackSets), then either replicate the S3 log buckets to a central account or set up cross-account CloudWatch Logs subscriptions. Keep the two aggregation paths mentally separate: CloudTrail metadata via the organization trail, content via a custom pipeline you own.

8. Distributing Controls at Scale: StackSets and CloudFormation Guard

Everything so far is per-account configuration. In an organization of dozens of accounts, hand-applying it does not scale and drifts immediately. Two mechanisms close that gap: StackSets to distribute the baseline, and CloudFormation Guard to validate it before it ships.

8.1 CloudFormation StackSets: push the baseline to every account

With service-managed permissions, StackSets integrates directly with AWS Organizations — no per-account IAM role wiring — and deploys a stack to every account in a target OU. A single governance baseline stack typically contains:
  • The organization's guardrail (or a reference to a shared one) so every account has the required GuardrailIdentifier available.
  • The model invocation logging configuration, its IAM delivery role, and the S3 buckets and CloudWatch log group it writes to — with AWS KMS encryption and least-privilege bucket policies.
  • The Bedrock-invoking role's IAM policy (approved model ARNs plus the required guardrail condition).
  • The interface VPC endpoint and its endpoint policy.

Two properties make StackSets fit governance:
  • Automatic deployment. With AutoDeployment enabled, any account newly added to a target OU automatically receives the baseline, and an account removed from the OU has the stack instances removed. New teams are governed on arrival, without a manual step.
  • Delegated administration. The management account can register a member account as a delegated administrator for StackSets, so a platform team can operate the baseline without management-account access. (Note the trade-off: a delegated administrator has broad deployment reach and cannot be scoped to specific OUs by the management account.)

aws cloudformation create-stack-set \
  --stack-set-name bedrock-governance-baseline \
  --template-body file://bedrock-governance-baseline.yaml \
  --permission-model SERVICE_MANAGED \
  --auto-deployment Enabled=true,RetainStacksOnAccountRemoval=false \
  --capabilities CAPABILITY_NAMED_IAM

aws cloudformation create-stack-instances \
  --stack-set-name bedrock-governance-baseline \
  --deployment-targets OrganizationalUnitIds=ou-root-workloads1 \
  --regions us-east-1 us-west-2
One caveat to design around: automatic deployment is a StackSet-level setting, applied uniformly to the targeted OUs — it is not tunable per account or per Region within the StackSet.

Creating the stack instances is not the end of the job — verify the rollout, then watch for drift. A governance baseline that failed to deploy in one account, or that an account administrator later modified out-of-band, is a silently ungoverned account: the missing-invocation-logs failure in section 9.3 is very often this. Verification and drift detection are both first-class StackSets operations:
# 1. Verify: per-instance status for the whole StackSet
aws cloudformation list-stack-instances \
  --stack-set-name bedrock-governance-baseline \
  --call-as DELEGATED_ADMIN
# each instance reports its status, status reason, drift status,
# and the timestamp of its last drift check

# 2. Detect drift across every stack instance (returns an OperationId)
aws cloudformation detect-stack-set-drift \
  --stack-set-name bedrock-governance-baseline \
  --call-as DELEGATED_ADMIN

# 3. Monitor the drift operation until it completes
aws cloudformation describe-stack-set-operation \
  --stack-set-name bedrock-governance-baseline \
  --operation-id <operation-id> \
  --call-as DELEGATED_ADMIN
Drift detection walks every stack instance and compares each deployed resource against the template, so it takes time proportional to the fleet — and only one drift-detection operation can run per StackSet at a time, which argues for a scheduled cadence (for example, a weekly EventBridge-triggered run) rather than ad-hoc invocation. When an instance reports drift, resolve it in one of two directions: re-run the deployment to stamp the baseline back over the out-of-band change, or — if the change was a legitimate local need — promote it into the template so the fleet converges on it deliberately. What you should not do is leave a drifted governance stack in place, because every control in this article that the baseline distributes (the logging configuration, the endpoint policy, the invoking role's guardrail condition) is only as real as the stack that deploys it.

8.2 CloudFormation Guard: gate the baseline in CI/CD

Distributing a template is only safe if the template is compliant. CloudFormation Guard (cfn-guard) is an open-source policy-as-code tool whose declarative Guard DSL validates JSON/YAML — including CloudFormation templates — against rules, in a pipeline, before deployment. A rule that requires the log bucket to be encrypted, for example:
let log_buckets = Resources.*[ Type == 'AWS::S3::Bucket' ]

rule bedrock_log_buckets_are_encrypted when %log_buckets !empty {
  %log_buckets {
    Properties.BucketEncryption exists
  }
}
Run it in the pipeline that ships the StackSet template:
cfn-guard validate \
  --data bedrock-governance-baseline.yaml \
  --rules bedrock-governance.guard
Position Guard correctly relative to its neighbors, because they are often confused:
  • cfn-lint checks syntax and resource-schema validity — not policy.
  • CloudFormation Guard checks policy compliance of the template before deployment — the CI/CD gate.
  • CloudFormation Hooks enforce policy server-side, at deploy time, inside CloudFormation itself — a runtime backstop that a pipeline check cannot replace.

Guard is the pre-deployment gate ("does this template enable logging and reference a guardrail?"); Hooks are the last-mile enforcement that catches anything deployed outside the pipeline. A mature setup uses both.

9. Failure Modes and Diagnostics

The controls above interact, and the interactions produce a small number of characteristic failures. For each: the symptom, the root cause, how to isolate it, and the fix.

9.1 Intermittent AccessDenied that correlates with load

Symptom. InvokeModel succeeds most of the time but fails with AccessDenied sporadically, more often under high load or during a Regional event.

Root cause. Cross-region inference is routing the request to a Region (or the "unspecified" pseudo-Region for Global CRIS) that your SCP or IAM policy does not allow. Because routing is dynamic, the denial appears only when Bedrock happens to route cross-region — hence the intermittency.

Isolation. Correlate the failing requests with the inference profile in use and check the CloudTrail event's awsRegion and errorCode. If failures cluster on a Region that is not the caller's, or if Global CRIS is enabled and aws:RequestedRegion was "unspecified", this is the cause.

Fix. Add "unspecified" to region-restriction SCPs for Global CRIS; for Geographic CRIS, allow the inference-profile ARN plus every destination Region's foundation-model ARN (Section 5.3).

9.2 Unintended Deny from a layered control

Symptom. A role that "should" be able to call a model is denied, and the identity policy looks correct.

Root cause. The denial comes from a different layer — an SCP Deny, a VPC endpoint policy that omits the model, a bedrock:GuardrailIdentifier mismatch, or an aws:SourceVpce condition when the call did not traverse the expected endpoint.

Isolation. Work outward from the identity policy. Check the CloudTrail errorCode / errorMessage for the failing call; an explicit Deny from an SCP, an endpoint policy denial, and a guardrail-condition mismatch surface differently. Confirm the request actually carried the expected guardrail identifier, and that it arrived over the intended VPC endpoint. Because an explicit Deny at any layer wins, the identity policy being correct is necessary but not sufficient.

Fix. Reconcile whichever layer produced the deny — most often either the endpoint policy's model list, a missing guardrail identifier on the request, or an SCP that is stricter than intended.

9.3 Missing invocation logs

Symptom. CloudTrail shows the InvokeModel calls, but the prompt/completion content is nowhere to be found.

Root cause. Model invocation logging is disabled by default and configured per-account, per-Region. A newly onboarded account, a newly used Region, or an account that drifted from the StackSet baseline will have no content logging even though CloudTrail (via the organization trail) still records the metadata. If content is logged but large bodies or images are missing, an S3 destination for large-data delivery was not configured.

Isolation. Confirm GetModelInvocationLoggingConfiguration returns a configuration in the specific account and Region, and that the relevant ...DataDeliveryEnabled toggles are on. Check whether the account is in the StackSet's target OU and whether the stack instance deployed successfully.

Fix. Enable logging via the StackSet baseline so every account and Region is covered, and configure an S3 large-data destination for image/large-text workloads.

9.4 A shared guardrail that works at home but fails cross-account

Symptom. Invocations that reference a centrally shared guardrail succeed in the account that owns the guardrail, but fail from consuming accounts — even though the consuming role's identity policy grants the guardrail-required InvokeModel and bedrock:ApplyGuardrail permissions.

Root cause. Cross-account guardrail use rests on two separate grants, and either one missing produces a failure that only appears outside the owner account. The guardrail's resource-based policy must allow the consuming principals (the aws:PrincipalOrgID / aws:PrincipalOrgPaths pattern from section 4.3). And if the guardrail is encrypted with a customer managed KMS key (section 10), the key policy must additionally grant those principals kms:Decrypt — a guardrail the consumer is allowed to reference but cannot decrypt still fails at inference time.

Isolation. Check which service produced the error in CloudTrail: a denial with a kms.amazonaws.com event source (or a KMS access-denied message on the Bedrock call) points at the key policy; a Bedrock-side access denial with the identity policy confirmed correct points at the guardrail's resource-based policy. Reproduce from an owner-account role to confirm the guardrail itself is healthy.

Fix. Treat the resource-based policy and the KMS key policy as one deployment unit: extend both to the same organization or OU scope, and ship them together in the same baseline that distributes the guardrail reference, so a new consuming account can never receive one grant without the other.

10. Cross-Cutting: Least Privilege, Data Protection, and Environment Separation

Three concerns run across every section above.

Least privilege for the roles that matter. The Bedrock-invoking application role should hold only InvokeModel / InvokeModelWithResponseStream on the approved model ARNs, plus ApplyGuardrail on the required guardrail — nothing broader. The model-invocation-logging IAM role should be able to write only to its specific log group and bucket. Separate the control-plane permissions (creating guardrails, changing logging configuration) into administrative roles distinct from the runtime roles.

Data protection for the logs. Because model invocation logs can hold pre-guardrail prompt content, they are among the most sensitive artifacts the platform produces. Encrypt them with AWS KMS, restrict read access with least privilege — a Basic / SensitiveData / Admin reader-role split is a practical pattern — and consider Amazon Macie over the log bucket. The most protective option is to not log prompt bodies at all for regulated inputs, relying on CloudTrail metadata and application-side redaction instead.

Data protection for the guardrail itself. A guardrail configuration is a sensitive artifact in its own right — its denied-topic definitions and regex patterns describe exactly what your organization is worried about — and it can be encrypted with a customer managed KMS key. The documented permission model distinguishes two personas: roles that create and manage guardrails need kms:Decrypt, kms:DescribeKey, kms:GenerateDataKey, and kms:CreateGrant on the key (granted in both the key policy and their identity policy), while roles that merely use the encrypted guardrail during inference need only kms:Decrypt. The key policy therefore looks like this:
{
  "Version": "2012-10-17",
  "Id": "KMS key policy",
  "Statement": [
    {
      "Sid": "PermissionsForGuardrailsCreators",
      "Effect": "Allow",
      "Principal": { "AWS": "arn:aws:iam::111122223333:role/GuardrailAdminRole" },
      "Action": [
        "kms:Decrypt",
        "kms:GenerateDataKey",
        "kms:DescribeKey",
        "kms:CreateGrant"
      ],
      "Resource": "*"
    },
    {
      "Sid": "PermissionsForGuardrailsUsers",
      "Effect": "Allow",
      "Principal": { "AWS": "arn:aws:iam::111122223333:role/BedrockInvokeRole" },
      "Action": "kms:Decrypt",
      "Resource": "*"
    }
  ]
}
In the multi-account pattern, this key policy is part of the sharing contract: every consuming account's invoke role needs the kms:Decrypt grant alongside the guardrail's resource-based policy, which is exactly the paired failure mode of section 9.4.

Environment separation. Use the account and OU boundary as the primary isolation mechanism: separate dev/stg/prd accounts, separate OUs for regulated workloads, and per-environment guardrails and endpoint policies distributed by StackSets. The account boundary is stronger than any in-account IAM boundary.

The limits of these controls — stated plainly

A security article that only lists controls is dishonest. Here is where each stops:
  • SCPs do not apply to every principal. They do not restrict the organization's management account, and they do not restrict service-linked roles. Do not assume an SCP covers everything in an account.
  • CloudTrail never contains prompt content. If your only audit is CloudTrail, you have metadata, not content. Content review requires model invocation logging, which is off by default.
  • Model invocation logging is per-account, per-Region, and opt-in. An account or Region you forgot to configure is silently unlogged, even while CloudTrail keeps recording metadata for it.
  • A required guardrail is not a screened input. Input-side bypass and multi-invocation orchestration APIs mean "guardrail attached" is not "content filtered on the way in."
  • Not every control is expressible as a condition key. Some intents (for example, restricting by model ID) have no reliable documented condition key and must be expressed by ARN and layered controls instead.

None of these are reasons to skip the controls. They are the reason the architecture is layered: organization → account → network → guardrail → audit, so that a gap in one layer is caught by another. "Passing every check" raises the floor; it does not certify safety.

11. Frequently Asked Questions

Do SCPs alone stop teams from using unapproved models?
For workload accounts, an SCP that denies non-approved foundation-model ARNs is an effective ceiling — no IAM policy in those accounts can broaden past it. But SCPs do not apply to the management account or to service-linked roles, so treat SCPs as the organization-wide floor and still apply per-account IAM restrictions.

How do I force every call through a guardrail?
Use the bedrock:GuardrailIdentifier condition key on InvokeModel / InvokeModelWithResponseStream, and grant bedrock:ApplyGuardrail on the guardrail. Be aware that input-side bypass and orchestrating APIs like RetrieveAndGenerate limit what "required guardrail" guarantees.

Can I restrict models with a bedrock:ModelId condition?
No — that condition key is not in the Bedrock Service Authorization Reference. Restrict models by their foundation-model ARN in the policy Resource / NotResource, which is the documented mechanism.

Why do my Bedrock calls fail only sometimes after I added a Region-restriction SCP?
Almost certainly cross-region inference routing to a Region (or the "unspecified" pseudo-Region) your SCP does not allow. Add "unspecified" for Global CRIS, and for Geographic CRIS allow the inference-profile ARN plus every destination Region's foundation-model ARN.

Does CloudTrail capture the prompt and the model's response?
No. CloudTrail records the invocation as metadata (modelId, identity, Region, source IP) with responseElements: null. Prompt and completion bodies are captured only by model invocation logging, which is disabled by default and configured per-account, per-Region.

Is model invocation logging organization-wide?
No. It is per-account and per-Region and off by default. Use CloudFormation StackSets to enable it everywhere, and build your own S3 replication or cross-account CloudWatch Logs subscription to centralize the content — the organization trail only centralizes CloudTrail metadata.

Does an endpoint policy replace IAM?
No — it is an independent, additional boundary. A request must satisfy IAM and the endpoint policy. Endpoint policies are useful for making the model allow-list a property of the network path and for pinning access to specific principals.

How do I find accounts that drifted from the governance baseline?
Run detect-stack-set-drift on the baseline StackSet (one drift operation per StackSet at a time), monitor it with describe-stack-set-operation, and read the per-instance drift status from list-stack-instances. A drifted governance stack means the account's logging, endpoint policy, or guardrail condition may no longer match what you think is deployed — re-deploy the baseline or deliberately promote the local change into the template.

Can a KMS-encrypted guardrail be shared across accounts?
Yes, but two grants must travel together: the guardrail's resource-based policy must allow the consuming principals, and the customer managed key's policy must grant them kms:Decrypt. Either one missing fails only in consumer accounts (section 9.4).

12. Summary

Governing Amazon Bedrock across an organization is a layered-control problem, not a single setting. The Multi-Account Bedrock Governance Foundation composes eight capabilities into one posture: SCPs cap model vendors and Regions organization-wide; AWS Organizations Bedrock policies enforce guardrail safeguards centrally across the organization; IAM identity policies grant approved model ARNs and make a guardrail mandatory with bedrock:GuardrailIdentifier; interface VPC endpoints and endpoint policies keep traffic private and enforce the model allow-list at the network; model invocation logging captures prompt/completion content while CloudTrail captures activity metadata; and StackSets plus CloudFormation Guard distribute and validate the baseline across every account.

The details that most often trip teams up are the honest ones: Converse is not a separate IAM action; bedrock:ModelId is not a real condition key; cross-region inference collides with Region-scoped policies unless you allow "unspecified" and every destination Region; CloudTrail holds no prompt content; and model invocation logging is opt-in per account and Region. Design for those, keep the controls layered, and remember that passing every check raises the floor without certifying safety.

13. References


Related Articles on This Site


References:
Tech Blog with curated related content

Written by Hidekazu Konishi