A tenant’s coding-agent role was supposed to be “S3 + Bedrock + one Lambda.” Someone widened it during an incident; the agent later called iam:CreateAccessKey and walked out with long-lived credentials. IAM identity policies alone are not a blast-radius ceiling when humans (or agents) can attach AdministratorAccess. AWS Organizations Service Control Policies (SCPs) are the unfair advantage: OU-level deny guards that apply even when the role is over-permissioned — agent accounts simply cannot call dangerous APIs. Pair with Verified Permissions, IAM condition keys, and Budgets — this post is the account blast-radius ceiling.
⚡ TL;DR: Put each tenant agent runtime in an account (or tightly scoped OU). Attach SCPs that deny
iam:*mutations,organizations:LeaveOrganization, broadkms:ScheduleKeyDeletion, and region escapes. SCPs do not grant access — they only clip the max permissions. Related: Verified Permissions Cedar, IAM condition keys, Lambda recursive loop protection.
Why identity policies are not enough
Defense in depth for coding agents:
| Layer | Answers | Failure mode if alone |
|---|---|---|
| Cedar / Verified Permissions | May this tool run for this user? | Tool runner still has fat IAM |
| IAM role + condition keys | May this principal call this API on tagged resources? | Human attaches Admin |
| SCP on agent OU | May anyone in this account call this API class? | Nothing — ceiling holds |
| Budgets / AppConfig | Spend + feature kill | Too late for IAM theft |
SCPs are max permission filters. They never grant. If SCP denies iam:CreateUser, no role in that account can CreateUser — including AdministratorAccess.
// ❌ Relying only on a "tight" agent role — next week someone attaches Admin
{
"Effect": "Allow",
"Action": ["s3:*", "bedrock:*", "iam:*"],
"Resource": "*"
}
OU layout for per-tenant agent accounts
Recommended skeleton:
RootSecurity/Shared(no agent workloads)AgentRuntimesOU ← SCP attached hereagent-tenant-acmeagent-tenant-globex
SandboxOU (different SCP, more deny)
Provision accounts with Control Tower / AFT / Account Factory. Agents never get organizations:* in identity policies; SCP still denies Leave/Remove.
// ✅ Conceptual: attach SCP to AgentRuntimes OU (Organizations SDK)
import {
OrganizationsClient,
AttachPolicyCommand,
} from "@aws-sdk/client-organizations";
const org = new OrganizationsClient({});
await org.send(
new AttachPolicyCommand({
PolicyId: "p-agentguard",
TargetId: "ou-xxxx-agentruntimes",
})
);
Deny dangerous actions even if roles are wide
Start with a deny list SCP for agent OUs (full-AWS-access SCP still required elsewhere in the hierarchy so allows can exist).
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "DenyIAMMutationsInAgentAccounts",
"Effect": "Deny",
"Action": [
"iam:CreateUser",
"iam:CreateAccessKey",
"iam:CreateLoginProfile",
"iam:AttachUserPolicy",
"iam:AttachRolePolicy",
"iam:PutRolePolicy",
"iam:UpdateAssumeRolePolicy",
"iam:PassRole"
],
"Resource": "*",
"Condition": {
"ArnNotLike": {
"aws:PrincipalARN": [
"arn:aws:iam::*:role/OrgBreakGlass*",
"arn:aws:iam::*:role/StackSet*"
]
}
}
},
{
"Sid": "DenyLeaveOrg",
"Effect": "Deny",
"Action": [
"organizations:LeaveOrganization"
],
"Resource": "*"
},
{
"Sid": "DenyDangerousAccountMoves",
"Effect": "Deny",
"Action": [
"organizations:DeregisterDelegatedAdministrator",
"organizations:DisableAWSServiceAccess"
],
"Resource": "*"
}
]
}
// ✅ Extra: deny regions you do not operate
{
"Sid": "DenyNonHomeRegions",
"Effect": "Deny",
"NotAction": ["sts:*", "cloudformation:*"],
"Resource": "*",
"Condition": {
"StringNotEquals": {
"aws:RequestedRegion": ["us-east-1", "eu-west-1"]
}
}
}
# ❌ SCP that allows iam:* "for convenience" in agent OU — deletes the ceiling
# Never put Allow iam:* on agent runtime OUs via SCP (SCPs rarely Allow anyway;
# the bug is usually forgetting Deny + attaching Admin at identity layer)
PassRole deserves special care: agents that can iam:PassRole into Lambda/CodeBuild can escalate. Deny PassRole broadly, then allowlist specific roles via condition iam:PassedToService + resource ARNs if you must.
Complement Verified Permissions and IAM condition keys
- Cedar: “user alice may invoke tool
apply_patchon repo X” - IAM conditions: “role may
s3:PutObjectonly ifs3:ExistingObjectTag/tenant=acme” (condition keys post) - SCP: “this account may not call
iam:CreateAccessKeyat all”
// ✅ Runtime still authorizes tools finely
import { VerifiedPermissionsClient, IsAuthorizedCommand } from "@aws-sdk/client-verifiedpermissions";
const vp = new VerifiedPermissionsClient({});
const decision = await vp.send(
new IsAuthorizedCommand({
policyStoreId: process.env.POLICY_STORE!,
principal: { entityType: "User", entityId: userId },
action: { actionType: "Action", actionId: "invokeTool" },
resource: { entityType: "Tool", entityId: "apply_patch" },
})
);
if (decision.decision !== "ALLOW") throw new Error("denied");
// SCP silently ensures even a buggy allow cannot CreateAccessKey
Recursive agent invokes and privilege loops still need Lambda recursive loop protection — SCPs do not stop application-level loops.
Rollout without locking yourself out
- Create SCP in detachable draft; validate JSON
- Attach to a pilot OU with one non-prod agent account
- Run agent integration suite: Bedrock, S3, Lambda invoke, CodeBuild start
- Watch CloudTrail for implicit denies (
implicitDeny/Org SCP) - Expand OU; keep break-glass roles exempt via
ArnNotLike - Document that management account / stacksets need exclusion paths
# ✅ Test effective permissions from agent role
aws iam simulate-principal-policy \
--policy-source-arn arn:aws:iam::TENANT:role/AgentToolRole \
--action-names iam:CreateAccessKey bedrock:InvokeModel s3:PutObject
# Expect CreateAccessKey denied by SCP even if identity allows
Pair spend ceilings with Budgets + Cost Anomaly — SCPs stop IAM escapes; Budgets stop token/GPU escapes.
What SCPs do not replace
SCPs will not:
- Stop an agent from deleting objects in buckets it is allowed to access
- Validate tool arguments (paths, SQL, shell) — that is application + Cedar
- Cap Bedrock token spend — use Budgets / Cost Anomaly and AppConfig
- Fix confused-deputy across accounts without resource policies
They will stop the common “agent escaped IAM” stories: creating users, planting access keys, attaching Admin to itself, leaving the organization to escape centralized CloudTrail, or spinning resources in unauthorized regions.
# ✅ Continuous control: EventBridge / Config rule on IAM API calls from agent accounts
# Any iam:CreateAccessKey from AgentRuntimes OU → PagerDuty (should be zero)
{
"Sid": "DenyS3PublicACL",
"Effect": "Deny",
"Action": ["s3:PutObjectAcl", "s3:PutBucketAcl"],
"Resource": "*",
"Condition": {
"StringEquals": {
"s3:x-amz-acl": ["public-read", "public-read-write"]
}
}
}
Document the break-glass path offline. If your only recovery role is also denied by SCP without an exemption, you will learn about SCP semantics at the worst possible time. Test restore of a mis-attached SCP quarterly.
Checklist
- [ ] Tenant agent workloads live under a dedicated OU with deny-list SCPs
- [ ] Deny IAM mutations, LeaveOrganization, and dangerous org APIs
- [ ] Exempt only break-glass / StackSet roles via tight ARN conditions
- [ ] Keep Cedar + IAM condition keys for fine-grained tool/resource auth
- [ ] Pilot on one account; simulate CreateAccessKey before broad attach
- [ ] Monitor CloudTrail for SCP denies; alert on any IAM API attempts from agent roles
Most viewed
- Python Decorators Explained: From Simple Wrappers to Production Patterns
- Distributed Locks Reality Check: When Redis Redlock Is the Wrong Tool
- Ai Differential Testing: AI Oracles Validated Against Shadow Traffic
- Spec-First AI Development: OpenAPI Remains the Only Source of Truth
- REST API Design Best Practices: The Patterns That Make APIs a Joy to Use
Newly added
- AWS Organizations SCPs: Hard Caps on What Coding-Agent Accounts Can Call
- Amazon Bedrock Prompt Management: Versioned System Prompts for Coding Agents
- AWS CodeArtifact: Private Package Mirrors Inside Coding-Agent Sandboxes
- Amazon MemoryDB: Durable Sub-Millisecond Session State for Multi-Turn Coding Agents
- AWS Lambda SnapStart: Cut Coding-Agent Cold Starts Without Always-On Waste
Deep-dive PDF
Get the expanded guide for this post — extra diagrams-style checklists, failure modes, and a production walkthrough. Free when you subscribe to CheatCoders.
Already subscribed? or open the subscribe page.
Discover more from CheatCoders
Subscribe to get the latest posts sent to your email.