This is the first post in our Amazon Bedrock series. Later posts cover Knowledge Bases, Agents, Guardrails, and choosing a foundation model.
A common first assumption about Amazon Bedrock is that it's "AWS's own AI model" — it isn't. Bedrock is a managed gateway to foundation models from multiple providers (Anthropic's Claude, Meta's Llama, Amazon's own Titan and Nova models, and others), accessed through one consistent API, billed through your existing AWS account, and governed by the same IAM permissions model you already use for every other AWS service.
Why reach for Bedrock instead of calling a provider's API directly
The OpenAI API, covered in an earlier series on this blog, requires its own separate API key, its own separate billing relationship, and its own separate access-control mechanism, entirely outside AWS. For an organization already running infrastructure on AWS, Bedrock's genuine value is consolidation: one IAM policy model, one bill, one set of audit logs (via CloudTrail), across every model provider Bedrock supports — rather than a different credential and a different security review for each model provider a team wants to evaluate.
Making your first call
import boto3
import json
client = boto3.client("bedrock-runtime", region_name="eu-west-2")
response = client.invoke_model(
modelId="anthropic.claude-3-5-sonnet-20241022-v2:0",
body=json.dumps({
"anthropic_version": "bedrock-2023-05-31",
"max_tokens": 300,
"messages": [
{"role": "user", "content": "Explain what a load balancer does, in two sentences."}
],
}),
)
result = json.loads(response["body"].read())
print(result["content"][0]["text"])Two details worth noticing immediately: boto3 — the same AWS SDK used for every other AWS service (S3, EC2, Lambda) — is the client here too, meaning a team already comfortable with boto3 for infrastructure work has no new SDK to learn just to call a foundation model. And the request body's exact shape is provider-specific — this example uses Anthropic's Claude message format because modelId specifies a Claude model; a Llama or Titan model expects a differently-shaped body, since Bedrock passes the request through to each provider's own underlying format rather than fully normalizing every provider behind one identical schema.
Model access: an explicit step, not automatic
Unlike calling OpenAI's API directly with an API key, Bedrock requires explicitly requesting access to each foundation model in the AWS Console before your account can invoke it:
AWS Console → Bedrock → Model access → Request model access
This is a deliberate governance checkpoint, not friction for its own sake — an organization can review and approve which specific models are permitted before any application code can actually call them, which matters for compliance-sensitive environments in a way that a per-provider, individually-managed API key doesn't naturally provide on its own.
IAM: the same permission model as everything else in AWS
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "bedrock:InvokeModel",
"Resource": "arn:aws:bedrock:eu-west-2::foundation-model/anthropic.claude-3-5-sonnet-20241022-v2:0"
}
]
}This is the exact same least-privilege principle applied throughout this blog — the scoped IAM user we built for our own GitHub Actions deploy pipeline, covered in our CI/CD case study — now applied to model access specifically: an application's IAM role can be restricted to invoking only specific, approved models, not a blanket "any Bedrock model" grant. A CI pipeline or Lambda function calling Bedrock inherits your organization's existing IAM controls directly, rather than needing a separate, parallel access-control system just for AI model calls.
Streaming responses
response = client.invoke_model_with_response_stream(
modelId="anthropic.claude-3-5-sonnet-20241022-v2:0",
body=json.dumps({
"anthropic_version": "bedrock-2023-05-31",
"max_tokens": 300,
"messages": [{"role": "user", "content": "Write a short paragraph about Kubernetes."}],
}),
)
for event in response["body"]:
chunk = json.loads(event["chunk"]["bytes"])
if chunk.get("type") == "content_block_delta":
print(chunk["delta"]["text"], end="", flush=True)The same streaming benefit covered in our OpenAI series — improved perceived latency, response rendered incrementally rather than all at once — applies here through invoke_model_with_response_stream, AWS's equivalent of the stream=True parameter from the OpenAI SDK.
What to actually remember from this post
- Bedrock is a managed gateway to multiple providers' models, not AWS's own proprietary model — Claude, Llama, and Amazon's own models are all accessed through the same service.
- The request body format is provider-specific, even though the client and authentication mechanism are unified through
boto3and IAM. - Model access must be explicitly requested and approved before an account can invoke a given model — a deliberate governance checkpoint.
- IAM policies can scope exactly which models a given role can invoke — the same least-privilege principle covered throughout this blog, applied directly to AI model access.
Next in the series: Amazon Bedrock Knowledge Bases: Building RAG Without Managing Infrastructure, where Bedrock handles the embedding and vector storage pipeline we built by hand earlier in this blog.
