# 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: `AppOwner` → `app-owner`,
`AvailabilityTier` → `availability-tier`, `BusinessUnit` → `business-unit`,
`CostCenter` → `cost-center`, `DataClassification` → `data-classification`,
`Project` → `project`). 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)
 − 3–5 CT guardrails (varies, set by Control Tower at registration)
 − 3  hca-security-baseline / hca-data-protection / hca-resource-lifecycle
 − 1  hca-tagging-policy
———
 = 1–3 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.
