Overview
I started with three accounts set up by hand in the console and almost no security: no working audit trail, no threat detection, no budget alarm, and leftovers from old experiments. I imported the organization into Terraform, added a dedicated security account, then hardened everything: fixing what I found, adding the security services and guardrails AWS recommends, and testing each one.
- management: runs the organization, single sign-on, billing and the policies every other account follows. No workloads.
- live: production, including this website.
- lab: deliberate experiments, logged and watched like everything else.
- security: the audit trail, the security tooling and the alerting hub.
Architecture
Guardrails flow down from management. Logs and security events flow into security, out of reach of anyone who takes over another account.
Tap or click the diagram to open it larger.
What I built
- Identity: single sign-on with MFA is the only way in. No IAM users or long-lived keys, and member accounts have no root passwords. I also found and removed two leftover roles that let one account take over the whole organization.
- Logs as evidence: one organization-wide trail into a log archive that can't be quietly changed: Object Lock, my own encryption key, and signed digests.
- Detection: GuardDuty in all 17 regions, Security Hub CSPM and AWS Config, with twelve rules that email me within minutes.
- Guardrails: organization policies that even account admins can't override: one region, small instances only, the baseline locked on, logs and alerts protected, and only my organization can reach my data.
- Changes and drift: every push is checked (format, Checkov, gitleaks), and a daily read-only plan in all four accounts catches anything changed outside the code.
- Respond and recover: new access keys switched off in seconds, a quarantine that freezes an account in one move, Terraform state nobody can wipe, and a tested break-glass path.
Tested, not assumed
- Every alert tested end to end with real events, not simulations.
- A staged 3 a.m. incident, run using only the runbook: contained, investigated, released. It found three gaps, now fixed or written down.
- Terraform state overwritten with junk, then restored to the last good version.
- A bucket locked with a deny-everyone policy, opened again through break-glass.
- A management root sign-in, and cutting off my own single sign-on user: both alerted.
Results
- 62 findings, every critical, high and medium one fixed. The critical one, a path from one account to the whole organization, I found by reviewing the roles myself, before running any scanner.
- Scanners as a second opinion: Prowler's failed checks down by a third (402 → 271), high-severity failures halved (79 → 39).
- Code: Checkov passes 168 checks with none failing; every skipped check has its reason in the code.
Limits
- Management is the one account organization policies can't reach. There it's single sign-on with MFA, alerts and break-glass.
- My admin role is broad (AdministratorAccess), limited by MFA and one-hour sessions.
- One security account instead of separate log-archive and tooling accounts: the first thing I'd split as it grows.
- Alerts are email only, with no SIEM.
Code and full write-up
The Terraform for all four accounts, the runbook, and the full write-up with every test.