A customer managed key on Bedrock does not stop AWS from processing your prompt. It was never meant to. What it changes is who can decrypt the copy of your data that sits at rest, and whether you can revoke that access on your own terms. People reach for a KMS key expecting privacy from Amazon, then discover the privacy guarantees they actually wanted were already there by default, and the key solves a different problem. This part sorts out which control does what.
Security on Bedrock is three separate questions that people mash into one. Who can see the data in flight and at rest. Where the data physically lives. And who can prove, later, what was sent and what came back. The controls are different for each, and reaching for the wrong one is how teams end up paying for a KMS key that does nothing they needed while leaving invocation logging switched off. I will take the three in order, from the default posture you already have to the controls you add on purpose.
What AWS can and cannot see in a Bedrock call
Start with the default, because it is stronger than most people assume. When you call a foundation model on Bedrock, the request travels over TLS 1.2 or higher, and Amazon does not retain your input or the model output as stored data once the call completes. The prompt and completion are not shared with the model provider, and they are not used to train the base model unless you explicitly opt in. Anthropic, Meta, and the other providers deliver their model into a per Region deployment account that the Bedrock service team runs, and the provider has no access to that account, so they never see your traffic or your logs. That is the part of the story that is already done for you.
What is not automatic is the data you deliberately persist. A knowledge base holds your documents in a vector store. A fine tune produces a private copy of the model weights. An agent stores its configuration. Those artifacts sit at rest in your account, and that is where encryption keys and IAM come in. The base inference call is transient. The things you build around it are not, and they are what you actually secure.
Where your prompts and outputs actually live
Residency on Bedrock is simple until cross-Region inference enters the picture. Under normal use, a call you make to us-east-1 is processed in us-east-1, and any resource you create there stays there. Fine tune a model in Frankfurt and the private copy of the weights remains in Frankfurt; your training data is transferred through your VPC, never crosses the public internet, and is encrypted in transit and at rest. If a rule says European personal data may not leave the EU, calling an EU Region satisfies it, provided you have not turned on routing that moves the request elsewhere.
That last clause is the one that catches people. A cross-Region inference profile, covered in Part 8, can route a single call to a second Region for capacity. A geographic profile keeps that routing inside a named boundary such as the EU, so residency holds. A global profile can send the request anywhere, which breaks a strict EU only guarantee. If residency is a hard requirement, use a geographic profile and confirm the destination in CloudTrail, where the processing Region is recorded in additionalEventData.inferenceRegion. Do not assume the Region you called is the Region that ran the model.
One more residency habit worth building. Model invocation logs, when you enable them, land in a CloudWatch log group or an S3 bucket that you choose, and Bedrock only supports a destination in the same account and Region as the calls. So your logs inherit the residency of the Region you configure them in. Put the bucket in the wrong Region and you have quietly created a copy of prompt and completion text outside your boundary.
Three kinds of key, and who holds them
AWS Key Management Service, KMS, is the service that creates and controls the encryption keys behind almost every AWS resource. Bedrock uses it through envelope encryption, meaning your data is encrypted with a data key, and that data key is itself encrypted by a KMS key. There are three kinds of KMS key, and the only thing that really separates them is control and visibility.
| Key type | Who controls it | You can audit use | Monthly key charge |
|---|---|---|---|
| AWS owned | AWS, shared across accounts | No | Free |
| AWS managed | AWS, in your account | Yes, via CloudTrail | Free |
| Customer managed (CMK) | You: policy, rotation, revoke | Yes, full control | 1 dollar per key |
Charges are US dollar list price. All three use the same AES 256 encryption. The difference is governance, not cryptographic strength.
The Bedrock default for stored resources is an AWS owned key. Your data is encrypted, but you cannot see the key, cannot set a policy on it, and cannot revoke it. That is fine for a lot of workloads. A customer managed key flips all three: you write the key policy that says which principals may use it, you decide the rotation schedule, and if you disable or schedule deletion of the key, every resource it protects becomes unreadable, including to AWS. That revoke switch is the real reason regulated teams pay the dollar a month.
When do you actually need a customer managed key?
Here is my rule after doing this on real accounts. Use a CMK when a written requirement forces one, and use the AWS owned default everywhere else. The requirements that force one are specific: a regulation or contract that says you must hold and be able to revoke the encryption keys yourself, a need to prove key access in your own CloudTrail, or a data perimeter design where key policy is one of the layers. If none of those apply, a CMK adds operational weight, a key policy you now have to maintain, a rotation you have to think about, a dependency that can take a resource offline, in exchange for a control nobody is asking you to have.
The trap is treating a CMK as a privacy upgrade over the default. It is not. Bedrock already does not train on your data or expose it to the provider, with or without your key. The CMK does not make the base call more private; it makes the resources at rest yours to lock. Buy it for the revoke switch and the audit trail, not for a confidentiality gain that the default already delivers.
My take
When I do use a CMK, I use one dedicated key per data domain, not one shared key across every Bedrock resource in the account. A shared key means one key policy has to serve every team, which drifts toward over broad grants, and one revoke takes down everything at once. Separate keys cost a dollar each and keep blast radius small. That dollar is the cheapest line item in the whole build.
Wire a CMK to a knowledge base
You attach a CMK at resource creation, per resource, through a named field. A knowledge base data source ingestion job takes kmsKeyArn. An agent takes customerEncryptionKeyArn. A model customization job takes customModelKmsKeyId, and an evaluation job takes customerEncryptionKeyId. There is no single account wide switch that turns CMK on for all of Bedrock; you set it where each resource is born. The example below creates a key, then points a knowledge base data source at it.
#!/usr/bin/env bash
# 1. Create a dedicated CMK for the knowledge base data domain
KEY_ARN=$(aws kms create-key
--description "bedrock-kb-support-docs"
--region us-east-1
--query KeyMetadata.Arn --output text)
echo "Created $KEY_ARN"
# 2. Attach it when creating the data source for the knowledge base
aws bedrock-agent create-data-source
--knowledge-base-id KB123456
--name support-docs
--region us-east-1
--server-side-encryption-configuration kmsKeyArn=$KEY_ARN
--data-source-configuration '{"type":"S3","s3Configuration":{"bucketArn":"arn:aws:s3:::my-kb-docs"}}'
kmsKeyArn echoed under the encryption configuration. Failure mode: an AccessDeniedException mentioning kms:GenerateDataKey means the key policy does not grant the Bedrock service and your ingestion role permission to use the key. Add them to the key policy, not to the role, because a CMK is gated by its own key policy first.Before you change production
Attaching or rotating a CMK on a live resource changes an encryption dependency. Create the key and test the full ingest and query path in a non production account first, confirm the key policy grants the Bedrock service principal and your roles, and keep the AWS owned default as a documented fallback. This is general guidance, not a substitute for your own change and security review. The most common CMK mistake is a key policy that grants the Bedrock service principal but forgets the ingestion role, so the query path works in testing while the sync job quietly fails to write. You notice only when the knowledge base stops updating.
What the CMK posture actually costs
Teams overestimate this line item, so here are real numbers. KMS charges a dollar per customer managed key per month, plus 0.03 dollars per 10,000 API requests, with the first 20,000 requests each month free. Bedrock drives KMS requests during ingestion, indexing, and query time decrypts on a knowledge base, not per token of inference. The worked example below sizes a single CMK on a moderately busy knowledge base.
Worked example
One CMK protecting a knowledge base that generates about 3,000,000 KMS requests in a month from ingestion and query decrypts. Key charge is 1 dollar. Billable requests are 3,000,000 minus the 20,000 free, so 2,980,000, which is 298 units of 10,000 at 0.03 dollars each, or 8.94 dollars. Total is 9.94 dollars for the month. Double the traffic and you add request cost only, not another key.
Compare that to the inference bill for the same workload, which runs in the hundreds or thousands of dollars. The security control is a rounding error. Cost is never the reason to skip a CMK; operational weight is.
Least privilege for Bedrock IAM
Encryption protects data at rest. IAM decides who can call the model and touch the resources in the first place, and it is where most real exposure lives. The common mistake is a role with bedrock:* on Resource: *, which lets any holder invoke every model, read every knowledge base, and change agent configuration. Scope it down. Grant the specific inference actions, pin the model ARNs the app is allowed to use, and separate the role that invokes models from the role that administers resources.
A least privilege policy for an app that only runs inference against two named models looks like this.
{
"Version": "2012-10-17",
"Statement": [{
"Sid": "InferenceOnly",
"Effect": "Allow",
"Action": [
"bedrock:InvokeModel",
"bedrock:InvokeModelWithResponseStream"
],
"Resource": [
"arn:aws:bedrock:*::foundation-model/anthropic.claude-sonnet-4-6-v1",
"arn:aws:bedrock:*::foundation-model/amazon.nova-pro-v1:0"
]
}]
}
Pair that with the network condition from Part 9, an aws:sourceVpce check, and you have two independent gates: the role can only invoke these two models, and only through your endpoint. When a CMK is in play, remember the key policy is a third gate. A principal needs the IAM grant to call Bedrock and the key policy grant to use the key, and a denial in either stops the request. That layering is the point, not an accident to work around.
Turn on invocation logging before you need it
Two logging layers matter and they answer different questions. CloudTrail records the control plane and API activity, who called what and when, and it is on for management events by default. It does not capture the prompt or completion text. Model invocation logging does, and it is off by default. Turn it on and Bedrock delivers the input and output bodies, up to 100 KB each, to a CloudWatch log group, an S3 bucket, or both, in the same account and Region. That is the record an auditor or an incident review will ask for, and you cannot log a call retroactively. Switch it on before the day you need it.
# Enable model invocation logging to CloudWatch and S3
aws bedrock put-model-invocation-logging-configuration
--region us-east-1
--logging-config '{
"cloudWatchConfig": {
"logGroupName": "/bedrock/invocations",
"roleArn": "arn:aws:iam::111122223333:role/bedrock-logging"
},
"s3Config": { "bucketName": "my-bedrock-logs" },
"textDataDeliveryEnabled": true
}'
get-model-invocation-logging-configuration shows your destinations. Test invocations then appear as JSON in the log group within a minute or two. Failure mode: a ValidationException about the delivery role or bucket policy means the logging role cannot write to the destination. Bedrock needs permission to deliver, so fix the role trust and the bucket policy, then retry.One caution that follows from the residency section. Invocation logs contain the actual prompt and completion text, so the log destination is now a store of potentially sensitive data. Encrypt that bucket or log group with a CMK if the source data warranted one, keep it in the right Region, and scope who can read it as tightly as the model access itself. A wide open log bucket undoes the encryption work upstream.
Start with AWS-owned keys, add a CMK only under a mandate
Here is the posture I ship. Take the default AWS owned key for every Bedrock resource unless a written requirement says you must hold and revoke your own keys, in which case use one dedicated CMK per data domain and grant the Bedrock service and your roles in the key policy. Pin your Region for residency, use a geographic inference profile if a rule bounds your data, and check the processing Region in CloudTrail rather than trusting the Region you called. Scope IAM to named actions and model ARNs, never bedrock:* on everything. And turn on model invocation logging now, to a destination in the right Region with access locked down, because it is the one control you cannot backfill.
The security story so far has been about protecting what you send and store. Next in the series I move from protecting the call to shaping what the model is allowed to say, with Bedrock Guardrails and content filtering. Before that, run get-model-invocation-logging-configuration in your main Region today. If it comes back empty, you have found your first gap.
Related reading: the vendor-neutral GenAI guide covers guardrails and responsible AI at the concept level, without the AWS specifics.
References
• Data encryption and key management, Amazon Bedrock User Guide
• Data protection, Amazon Bedrock User Guide
• Monitor model invocation with CloudWatch Logs and Amazon S3
• AWS Key Management Service pricing


DrJha