soft guideINFRASTRUCTURE & ADVISORY

Note 08

Guardrails an account inherits on day one

Organization-wide SCP and RCP guardrails consolidated from per-account copies into one baseline per OU, with offline tests and a dry run against real CloudTrail history.

In the platform
Secure package · AWS guardrails module, also sold on its own
Experience behind it
Audited the existing policies, designed the target layout and the rollout, built the verification tooling.
Rootroot protection · data perimeterWorkloads OUboundaries · regions · IMDSv2 · encryptionSuspended OUdeny allDevelopmentStagingProductionCI-only images · code-only edges

How guardrails rot

Guardrails grow account by account: the same rule copied with a different list of ARNs, accounts at the five-policy quota, new accounts with none at all. Nobody can say what an account actually has without reading every policy on its path.

One rule, one place

Each rule lives once, at the highest level of the organization where it is true:

SCPs have no audit mode

A broken SCP is found by an outage. So every change passes three gates. Offline tests check the quota, that every statement is a Deny, and that the break-glass role keeps a way in. A replay of ninety days of CloudTrail lists every principal the candidate would have denied. And new policies are attached next to the old ones for 48 hours before anything is removed, OU by OU, production last; policies combine as a union of denies, so this never opens a gap.

run "guardrails_only_deny" {
  command = plan
  assert {
    condition = alltrue(flatten([
      for p in aws_organizations_policy.scp : [for s in jsondecode(p.content).Statement : s.Effect == "Deny"]
    ]))
    error_message = "An SCP contains an Allow statement; guardrails only take permissions away."
  }
}

The replay is the gate that pays for itself: it catches the change that would have stopped node autoscaling in production because one role name did not match a new pattern.

What it changes

Dead and duplicated policies went first, cutting the custom set by a third with no change in effective access. The target layout takes it from nineteen policies to six and from thirteen account-level attachments to one deliberate exception, rolled out OU by OU. The dry run has already paid for itself: it caught a change that would have stopped node autoscaling in production before it shipped.

Start a conversation

Bring your infrastructure
into focus.

Tell me what you are building, operating, or trying to improve.

Let’s talk