README

HCA SCP Consolidation — Summary

Background

Started with 14 SCP files (13 custom + FullAWSAccess). Root cannot be used for attachment — unmanaged accounts sit there too, so anything attached at root risks collateral impact outside this project's scope. All attachment happens at Level-1 OUs (or below); each OU/account inherits from its parent OU, not from root directly, for anything root doesn't already carry.

FullAWSAccess is inherited from root and does not count against any OU's attachment quota. CT-managed guardrails (aws-guardrails-*) are never touched — no attach, detach, or edit — Control Tower owns their lifecycle entirely.

AWS raised the per-node SCP cap from 5→10 and the size cap from 5,120→10,240 characters on 2026-05-15. All numbers below reflect the current (raised) limits.

What got consolidated

10 custom SCPs → 3 merged documents, grouped by function. 1 tagging policy kept standalone (large, mid-rollout, changes independently of security rules).

hca-security-baseline.json

Merged from:

  • Deny_ability_to_disrupt_GuardDuty
  • Deny_disabling_CloudWatch_or_altering_its_configuration
  • Protect_security_settings
  • LeaveOrg

hca-data-protection.json

Merged from:

  • Deny_unencrypted_EBS_volume_creation
  • Deny_public_RDS_creation
  • S3_SSl_Policy

hca-resource-lifecycle.json

Merged from:

  • deny-delete-actions
  • Deny_ec2_imdsv_1

hca-tagging-policy.json

Not merged with the above — kept as its own document. Cleaned up from the original (kebab-cased all tag keys for consistency: AppOwnerapp-owner, AvailabilityTieravailability-tier, BusinessUnitbusiness-unit, CostCentercost-center, DataClassificationdata-classification, Projectproject). Originally split into two files to fit the old 5,120-byte cap; re-merged into one file once the 10,240-byte cap confirmed it fits (9,347 bytes pretty-printed). Still unattached anywhere — mid-rollout.

Requires one Deny statement per required tag by design — AWS evaluates multiple condition keys under one operator with AND logic, so a single statement checking several tags for Null would only fire when every tag is missing at once, not when any one is. Splitting per-tag is what makes "deny if any required tag is missing" actually work.

Net result

Before: 10 custom SCPs consuming up to 6 slots per OU (varies). After: 4 documents (3 merged + 1 tagging), same enforcement, fewer slots.

CT guardrail footprint (informational — not something to attach/detach)

Snapshot from the org structure pull, for the 8 project-scoped OUs. Shows what Control Tower already occupies at each OU, so remaining headroom is clear before attaching the 4 files above to a new OU.

OU CT guardrails present CT slot count
Data utQMHg, hiQAAt, kwDsmG, GEFrgv, PrFwNu 5
Sandbox yvlcHf, utQMHg, URVMlf, PrFwNu 4
Integration ipsuXu, PrFwNu, rDMgmy 3
Security Vzvcwh, utQMHg, chQKXg, GEFrgv 4
Legacy utQMHg, qFyGLz, yuPdpk, GEFrgv, PrFwNu 5
DR onCsAG, AozcGY, utQMHg, GEFrgv, PrFwNu 5
Finance QaqgVP, PrFwNu, fgKDjS 3
Platform HeQGNC, utQMHg, wUAwns, PrFwNu 4

Three guardrails recur across most OUs:

  • aws-guardrails-PrFwNu (p-vswc3nsu, GRREGIONDENY) — 7 of 8 OUs
  • aws-guardrails-utQMHg (p-dp5q0qb6) — 6 of 8 OUs
  • aws-guardrails-GEFrgv (p-vhl7fpu9) — 4 of 8 OUs, same content as utQMHg (known duplicate pair, flagged separately — not actioned here)

Each OU also carries 2 additional CT guardrails unique to it (not shared with any other project OU), likely part of Control Tower's per-OU baseline set applied at registration.

Slot math for attaching the 4 consolidated files to a NEW OU

Assuming a new OU picks up a similar CT baseline (FullAWSAccess free + ~3-5 CT guardrail slots, actual count depends on what Control Tower assigns):

10 (cap)
  0  FullAWSAccess (inherited from root, free)
  35 CT guardrails (varies, set by Control Tower at registration)
  3  hca-security-baseline / hca-data-protection / hca-resource-lifecycle
  1  hca-tagging-policy
———
 = 13 slots free, depending on the new OU's CT guardrail count

Confirm the new OU's actual CT footprint (via list-policies-for-target) before attaching, since the count isn't fixed across OUs.