A guardrail you haven't tried to violate is just documentation with better formatting.
That's the premise behind aws-boundary: an AWS multi-account setup using AWS Organizations, where a management account defines security guardrails and a separate member account operates under full administrator access, yet remains strictly bound by policies it cannot edit, bypass, or disable.
The setup
One management account defines two Service Control Policies attached to a workloads organizational unit:
-
a region lock (deny anything outside
us-east-1, with exemptions for global services like IAM and STS) and - CloudTrail immutability (deny stopping, deleting, or modifying the audit trail, from anyone, including administrators).
A member account, dev-workload, gets native AdministratorAccess inside its own boundary. Access is brokered exclusively through short-lived STS AssumeRole sessions, zero static IAM user access keys allowed.
The experiment focuses on the gap between what AdministratorAccess implies on paper and what an SCP actually enforces at the API evaluation layer underneath it.
Proving it, not just configuring it
I assumed the admin role in the member account and tried to do exactly the things the guardrails are supposed to prevent, from inside the account that's supposed to be blocked:
-
Permitted Control Test (
us-east-1): Created an SQS queue in the allowed region. Succeeded with HTTP 200, confirming the boundary doesn't break legitimate operations. -
Violation 1 (Region Lock Breach): Attempted to create an SQS queue in
eu-west-1. Failed with an explicit deny:
User: ... is not authorized to perform: sqs:CreateQueue ...
with an explicit deny in a service control policy: arn:aws:organizations::...:policy/.../p-oajpewkp
-
Violation 2 (Audit Tampering): Attempted
cloudtrail:StopLoggingagainst the organization trail. Denied immediately withAccessDeniedException. -
Violation 3 (Audit Destruction): Attempted
cloudtrail:DeleteTrail. Denied immediately withAccessDeniedException.
Four tests, four expected outcomes. The same explicit denial triggered inside the AWS Management Console UI, rendering an unbypassable red banner when attempting to switch regions.
What actually went wrong along the way
None of this worked on the first attempt, and the failures were more useful than the eventual pass.
Attaching the SCPs initially failed with PolicyTypeNotEnabledException, because feature_set = "ALL" makes SCPs available at the organization level, but the policy type still has to be explicitly enabled on the root container separately, a distinction that isn't obvious until Terraform tells you no.
Tearing the environment down surfaced two more: an OU can't be deleted while a suspended account is still parented under it, AWS holds closed accounts in a 30-day suspended state, and an organization itself can't be dissolved while any suspended account exists anywhere inside it. The actual fix for the second one was uncomfortable: leave the empty organization shell in place at zero cost, and remove it from Terraform state rather than fight AWS's retention policy.
Why the region-lock exemption list matters more than it looks
The region-lock SCP isn't a blanket deny. It explicitly exempts global control-plane services, IAM, STS, Organizations itself, Route 53. Get that exemption list wrong and you don't get a stricter guardrail, you get an account that can't authenticate or manage its own identity, which is a worse failure mode than the one you were trying to prevent. A security control that locks you out of fixing itself isn't a security control, it's an incident.
What this project didn't try to be
This isn't a full landing-zone framework, and two accounts under one OU is a small fraction of what a real multi-account AWS environment looks like at scale, more OUs, more granular SCPs, config aggregators, security hub integration. What it does prove is narrow and specific: that a policy attached at the organization level actually holds against an administrator trying to work around it from inside a member account, verified by trying, not by reading the policy and assuming it's correct.



Top comments (0)