Earlier this month I published on the AWS Security Blog with Rodolfo Brenes, Satish Uppalapati, and Sowjanya Rajavaram: Operationalizing least privilege: Automate IAM remediation through your CI/CD pipeline.
The short version: IAM Access Analyzer already tells you which permissions a role is not using. Most teams never act on it. The findings pile up, nobody is sure who owns the role, and tightening a policy by hand is slower than leaving it alone. Detection was never the bottleneck. Remediation is.
The part I think is worth calling out is how the solution decides what to do with a finding. It does not treat every over-permissioned role the same way. It first asks where the role came from, by reading the CreateRole event in CloudTrail. A role created by CloudFormation shows a service principal of cloudformation.amazonaws.com. A role created by a person shows an IAM user and a console or CLI user agent.
That one question changes the work. If the role is managed as code, the fix belongs in the repository, so the automation uses Amazon Bedrock to turn the Access Analyzer recommendation into CDK and opens a pull request against the right repo. If someone created the role by hand, there is no repository to change, so it opens an issue with the recommended policy and guidance on moving the role into infrastructure as code. If the role is unused entirely, it opens a decommission issue.
Fixing a role in the console when the role is defined in CDK does not hold. The next deployment puts the old permissions back. Sending the finding to the pipeline is what makes the change stick.
The unused-role path is deliberately slow. Rather than delete, the automation recommends attaching a deny-all policy, watching for 30 days, and only then removing the role. Some roles run quarterly. Some run at year end. A role that looks dead in September may be load-bearing in December.
A few other decisions from building it: the run is capped by default, at 50 findings and 10 unused-role issues, because a first run against a real account can surface hundreds and drown the team that has to review them. There is a dry-run mode for tuning exclusions before anything reaches a repository. And when a managed policy is shared across several roles, the automation stops and files an issue instead of opening a pull request, because narrowing that policy for one role can break the others.
The full post has the architecture and the walkthrough. The code, the CDK stacks, and the configuration templates are on GitHub: sample-operationalizing-least-privilege-iam-remediation.