The context
A French B2B fintech, regulated, 80 people including 25 engineers. An annual regulator audit was scheduled in 8 weeks, with an announced focus on production access security.
The AWS organization had 12 accounts, 280 IAM users, and an undetermined number of long-lived access keys scattered around. No SSO, no SCPs, no organization-wide audit trail. A 4-year heritage of fast growth where each team had created its own roles by its own conventions.
The internal team knew what needed to be done. They simply didn’t have the bandwidth to run the migration alongside the product roadmap, and the auditor was coming.
Week 1 diagnosis
| Finding | Risk | Priority |
|---|---|---|
| 280 IAM users with console access | Attack surface + fuzzy offboarding | P0 |
| 47 long-lived (>90d) IAM access keys | Persistent leak risk | P0 |
| Zero org-wide SCP | No anti-bypass guardrails | P0 |
| Per-account CloudTrail, no centralization | Incident reconstruction impossible | P1 |
6 cross-account roles with Action: "*" | Massive blast radius | P0 |
| MFA not mandatory on 35% of users | Compliance non-conformance | P0 |
| No periodic access reviews | Compliance non-conformance | P1 |
Verdict: the IAM posture wouldn’t last 30 minutes in audit. But the scope was bounded and addressable in 6 weeks with a dedicated team.
The intervention
Weeks 1-2 — IAM Identity Center foundation
Activation of AWS IAM Identity Center at the organization level, SAML federation with the existing IdP (Okta), already used for other internal SaaS.
Definition of 6 permission sets matching role profiles:
BillingReadOnly,DeveloperAccess,DataAnalyst,SecurityAuditor,Operator,Administrator
Each permission set versioned in Terraform. Sessions limited to 8h (4h for Administrator). MFA mandatory at IdP level, propagated to SSO.
resource "aws_ssoadmin_permission_set" "developer" {
name = "DeveloperAccess"
instance_arn = local.identity_center_arn
session_duration = "PT8H"
}
resource "aws_ssoadmin_managed_policy_attachment" "developer_powers" {
instance_arn = local.identity_center_arn
managed_policy_arn = "arn:aws:iam::aws:policy/PowerUserAccess"
permission_set_arn = aws_ssoadmin_permission_set.developer.arn
}
Week 3 — Org-wide SCPs
Three SCPs deployed at the Organizations level:
- DenyRegionsOutsideEU: forbids use of regions outside
eu-west-1/eu-west-3(except global services IAM/CloudFront/Route53). Data localization compliance. - DenyRootUser: no action allowed for the root user except in 4 sensitive accounts, where a documented override SCP applies.
- DenyDisablingSecurityServices: no disabling of CloudTrail, Config, GuardDuty, Security Hub. Even for Administrator.
Progressive rollout: Sandbox OU first (test), then Workloads in dry-run mode via Service Last Accessed, then hard enforcement.
Week 4 — Human access migration
The critical operation. 280 IAM users to remove, without breaking access overnight.
The cutover ran by OU over 5 days:
- Day 1-2: Data + BI teams (40 users) — console-only, simple flip
- Day 3: Dev teams (180 users) — early communication, shared runbook, visible escape hatch
- Day 4: Ops + SecOps (50 users) — coordinated with on-call rotations
- Day 5: Admins + Auditors (10 users)
For each removed IAM user, documented mapping to their Okta group + permission set. Zero access loss, zero critical support tickets during the window.
Week 5 — Access keys and roles
47 long-lived IAM access keys identified via the Credential Report. Categorization:
| Usage | Action |
|---|---|
| 12 GitHub Actions CI/CD keys | OIDC migration, deletion within 7 days |
| 8 GitLab CI/CD keys | OIDC migration, deletion within 7 days |
| 15 third-party tools (monitoring, backup) | Rotation to assumable roles with ExternalId, 1h duration |
| 9 ad-hoc human-use keys | Immediate deletion (already covered by SSO) |
| 3 legacy app keys | Migration to IAM Roles for EC2/Lambda |
All keys rotated or removed in week 5. Credential Report went from 47 to 0 long-lived access keys on human principals.
Week 6 — Org CloudTrail + audit trail
Creation of a dedicated Log Archive account, partially existing but not org-wide-scoped. Migration of all per-account CloudTrails to a single organization-wide trail:
resource "aws_cloudtrail" "organization" {
name = "organization-trail"
s3_bucket_name = aws_s3_bucket.log_archive.id
is_organization_trail = true
is_multi_region_trail = true
include_global_service_events = true
enable_log_file_validation = true
}
S3 bucket with Object Lock COMPLIANCE 7 years, read-only access from the audit account. GuardDuty and Security Hub findings also aggregated to the audit account.
Audit outcome
The audit ran 2 weeks after mission end. The IAM scope held up to 4 hours of examination without difficulty:
- 0 residual human IAM user, 100% access via SSO + MFA
- 0 long-lived access keys on human principals, machines via OIDC or assumable roles only
- 3 documented and applied SCPs, with proof of planned quarterly review
- Org-wide CloudTrail with 7-year retention, integrity validated
- Permission sets versioned in Git, full change traceability
The auditor reported 0 critical findings on the IAM scope. Two minor observations on access review frequency (quarterly instead of monthly) — corrected before the report write-up.
Lessons we apply systematically
Three principles confirmed by this engagement:
- Audit urgency doesn’t license sloppy work. Everything we did is still in place 18 months later, because we invested the time to migrate properly via Identity Center and Terraform rather than surface-patching.
- The escape hatch is non-negotiable. Before every cutover (OU, SCP, access key removal), a signed rollback runbook. That’s what lets you move fast without breaking things.
- Humans are the rate limiter. The IAM Identity Center technical migration takes 5 days. Change management (training, runbooks, communication, support tickets) takes the remaining 5 weeks. Underestimating that part means a clean infra and hostile teams.
The client has since integrated quarterly permission set reviews into their engineering cycle, and added a Security Hub dashboard shared weekly with the CISO.
"We had 18 months of accumulated IAM debt and 6 weeks before the auditor. Distribuée delivered the full migration without disrupting our team — they worked alongside us, not instead of us. The auditor found zero critical issues on the IAM scope."
Different situation?
Let's discuss for 15 minutes.
Every engagement is scoped individually after a no-strings call.
Book a slot