Product accounts should not share a Bedrock API key on Slack. Centralize model access in a platform account: SCPs, IAM role assumption, application inference profiles, and cost tags per team. Consumers get short-lived credentials and quotas — never long-lived keys.
⚡ TL;DR: Platform account owns Bedrock + Guardrails + Prompt Management. Product accounts assume
BedrockConsumervia STS with external ID; tag every callteam,service,env. Use application inference profiles for per-team routing and spend. Pair with LLM Cost Controls and AI Coding in VPC.
Account layout
org
├── platform-ai (Bedrock, Guardrails, KMS, CloudTrail lake)
├── product-payments (assume role → invoke)
└── product-growth (assume role → invoke)
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": { "AWS": "arn:aws:iam::111111111111:root" },
"Action": "sts:AssumeRole",
"Condition": {
"StringEquals": { "sts:ExternalId": "payments-prod-extid" },
"IpAddress": { "aws:SourceIp": "10.0.0.0/8" }
}
}]
}
{
"Effect": "Allow",
"Action": [
"bedrock:InvokeModel",
"bedrock:InvokeModelWithResponseStream",
"bedrock:Converse",
"bedrock:ConverseStream"
],
"Resource": [
"arn:aws:bedrock:*::foundation-model/anthropic.*",
"arn:aws:bedrock:*:999999999999:inference-profile/payments-*"
],
"Condition": {
"StringEquals": {
"aws:RequestTag/team": "payments",
"aws:RequestTag/env": ["prod", "staging"]
}
}
}
Consumer SDK pattern
import { STSClient, AssumeRoleCommand } from "@aws-sdk/client-sts";
import { BedrockRuntimeClient, ConverseCommand } from "@aws-sdk/client-bedrock-runtime";
import { fromTemporaryCredentials } from "@aws-sdk/credential-providers";
export function bedrockForTeam(team: string) {
return new BedrockRuntimeClient({
region: "us-east-1",
credentials: fromTemporaryCredentials({
params: {
RoleArn: process.env.BEDROCK_CONSUMER_ROLE_ARN!,
RoleSessionName: `bedrock-${team}`,
ExternalId: process.env.BEDROCK_EXTERNAL_ID!,
Tags: [
{ Key: "team", Value: team },
{ Key: "env", Value: process.env.APP_ENV! },
],
},
}),
});
}
❌ Hardcoding access keys in Parameter Store “shared with eng”.
SCPs that keep product accounts honest
Deny bedrock:* in product accounts except through VPC endpoints to the platform pattern you choose — or deny local Bedrock entirely and force cross-account invoke. Deny iam:CreateAccessKey for humans in prod OUs.
Attribution and quotas
| Mechanism | Purpose |
|---|---|
| Application inference profiles | Per-team model routing + metering |
| Cost allocation tags | Showback/chargeback |
| CloudWatch metrics by team | Burn alerts |
| SCPs | Prevent shadow Bedrock in rogue accounts |
See spend alerts in LLM Cost Controls.
Closing checklist
✅ Dos
– ✅ Central Bedrock in platform account
– ✅ STS assume-role + external ID + session tags
– ✅ Inference profiles per team
– ✅ Guardrails applied centrally
– ✅ CloudTrail + spend dashboards by tag
❌ Don’ts
– ❌ Don’t share long-lived access keys across teams
– ❌ Don’t allow untagged InvokeModel
– ❌ Don’t let product accounts create their own foundation-model wildcards unchecked
– ❌ Don’t skip ExternalId on the trust policy
Related reading
- LLM Cost Controls: Token Budgets Per PR and Per Engineer
- AI Coding in VPC: Private Bedrock Endpoints and Secret Hygiene
- Amazon Bedrock Agents: Tool Use, Memory, and Production Guardrails
- Bedrock Guardrails: Block Prompt Injection Inside Internal Dev Tools
Last updated on September 11, 2026
Discover more from CheatCoders
Subscribe to get the latest posts sent to your email.

Pingback: Multi-Tenant Coding Assistants: Isolated ECS Fargate Spot Runtimes - CheatCoders
Pingback: Private Model Hosting: When Bedrock Alone Cannot Meet Constraints - CheatCoders