Amazon Bedrock in Practice

IAM Service Roles for Lambda, ECS, and EC2 Calling Bedrock

Production code should never carry API keys to call Claude on Bedrock. The compute it runs on already has an identity — the job is attaching the right role with the minimum permissions.

Claude 3P 101 · Updated July 2026 · Unofficial guide

One of Bedrock's biggest operational advantages over a standalone AI vendor is that authentication disappears into AWS's existing machinery. A Lambda function, an ECS task, or an EC2 instance each carries an IAM role, and the Anthropic SDK resolves credentials through the standard AWS chain — constructor arguments, then environment variables (AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_SESSION_TOKEN, AWS_REGION), then the config file and credential providers including SSO, assumed roles, the ECS task role, and the EC2 instance metadata service. In practice: attach a role to the compute, write no credential code at all.

The role per compute type

The pattern is the same everywhere; only the attachment point differs. For Lambda, add Bedrock permissions to the function's execution role. For ECS/Fargate, put them on the task role (the identity your application code uses) — not the task execution role, which is what the ECS agent uses to pull images and write logs. For EC2, attach an instance profile wrapping the role; the SDK picks it up from instance metadata. In all three cases, this Python runs unchanged:

from anthropic import AnthropicBedrockMantle

# No keys anywhere: credentials come from the attached role.
client = AnthropicBedrockMantle(aws_region="us-east-1")
msg = client.messages.create(
    model="anthropic.claude-sonnet-5",
    max_tokens=512,
    messages=[{"role": "user", "content": "Summarize this ticket..."}],
)

A least-privilege inference policy

Avoid attaching AmazonBedrockFullAccess to production workloads — it exists, alongside AmazonBedrockReadOnly, but grants far more than inference. A workload that only calls Claude needs the invocation actions, scoped to the models it uses:

{
  "Version": "2012-10-17",
  "Statement": [{
    "Effect": "Allow",
    "Action": [
      "bedrock:InvokeModel",
      "bedrock:InvokeModelWithResponseStream"
    ],
    "Resource": [
      "arn:aws:bedrock:*::foundation-model/anthropic.claude-sonnet-5",
      "arn:aws:bedrock:us-east-1:111122223333:inference-profile/*"
    ]
  }]
}

Three notes on that policy. First, if the workload uses the Converse API on the legacy surface, bedrock:Converse and bedrock:ConverseStream exist as actions too — though AWS documents that Converse is blocked automatically when InvokeModel is denied, allow statements should name what the code actually calls. Second, the inference-profile ARN line matters: invoking through a cross-region inference profile requires permissions on both the foundation-model and inference-profile resources. Third, if the workload uses the newer "Claude in Amazon Bedrock" surface, note that the bedrock-mantle endpoint uses a distinct action namespace — bedrock-mantle:CreateInference — so a policy written only with bedrock:* actions won't cover it. Grant that action on the allowed model ARNs for roles using AnthropicBedrockMantle.

Rule of thumb: one role per workload, one model family per role, both endpoints' actions only if both are genuinely used. Shared "AI role" ARNs across teams defeat cost attribution and blast-radius control alike.

The provisioned service-role pattern

For the current Claude in Amazon Bedrock surface, Anthropic documents a recommended variant for human and CI access: an administrator provisions a Bedrock service role with the inference permissions, and grants developers iam:PassRole on that role rather than direct invocation rights. This centralizes what the role may do while letting many principals use it. Federated alternatives (SAML/OIDC/Identity Center assumed roles, 12-hour maximum session) and short-lived bearer tokens exist for the same surface, with bearer tokens explicitly the least-preferred option — compute workloads should stay on attached roles.

Rotation is another quiet benefit of this pattern. Roles issue temporary credentials that AWS refreshes automatically, so there is no long-lived Claude secret to store, leak, or rotate on a schedule — a meaningful reduction in operational risk compared with API-key-based access to a standalone vendor, and one reason security teams tend to approve Bedrock-based Claude deployments quickly.

Hardening around the role

Two complements finish the design. Network: if the compute runs in a VPC without internet egress, add interface VPC endpoints (com.amazonaws.{region}.bedrock-runtime or com.amazonaws.{region}.bedrock-mantle) so Claude traffic stays on AWS PrivateLink — details in PrivateLink setup. Audit: CloudTrail logs InvokeModel and Converse calls as management events by default, including the calling role's identity, so per-role usage is traceable without extra setup.

Where to go next

Restrict roles further with Bedrock IAM condition keys, or centralize inference across accounts with cross-account role assumption. General principles live in least-privilege IAM for Claude.

Sources