Section 01The user I was trying to fix
naresh lives in one of my sandbox accounts. Ten permissions policies attached, AdministratorAccess at the top of the list, console access enabled without MFA, and two access keys that are eleven days old and have never been used.
Nobody needs tooling to tell them this is too much. The useful question is the harder one: what does this user actually do? Taking AdministratorAccess away is a two-click job. Knowing what to put back without breaking the person on the other end is what stalls every least-privilege project I have worked on.
IAM will answer that from CloudTrail. The feature has been sitting in the console for years without much attention.
Section 02Where the generator hides
It is at the bottom of the user's Permissions tab, under the permissions boundary panel, far enough down the page that I have scrolled past it plenty of times.
That "no requests in the past 7 days" line tripped me up for a second. It is the history of generation requests, not of the user's activity.
Section 03The wizard, and the trail I did not have
Three decisions on one screen: how far back to look, which trail to read, and which role IAM assumes to read it.
The window is either a rolling number of days or an explicit range capped at 90.
I picked 1 day because the activity I wanted was from that afternoon and I wanted a fast loop. For a real access review that number is wrong. Go to 90, and accept that even 90 days misses quarter-end and anything annual.
Then the trail dropdown came up empty.
Section 04Creating the trail
Quick trail create takes about fifteen seconds. Name it, accept the bucket it generates for you, done. Anything created in the console is multi-region.
Logging management events costs nothing. The S3 bucket underneath it does, and a multi-region trail in a busy account writes more than people expect.
What quick create skips is everything that makes the trail worth trusting.
Going through the full Create trail workflow instead lets you turn on SSE-KMS with a customer managed key and switch on log file validation.
The KMS key and the bucket have to live in the same region. The console does say so, but it says so quietly, right after you have already picked a key.
Validation disabled and no encryption is fine for a lab and not fine for anything an auditor will open. If this trail were staying, that is the first thing I would change.
Section 05Running the generator
Two things here cost me time.
The trail's home region is Mumbai, but it shows up under N. Virginia in this dropdown too. That is the multi-region trail's shadow copy. Same trail, so pick whichever region surfaces it.
The blue callout matters if your logs land in a central logging account. The service role IAM creates needs read access to that bucket in the other account, and this wizard does nothing to set that up for you. Sort out the bucket policy first or the run fails.
Only the regions you tick get reviewed. Miss one and you will generate a confidently incomplete policy. Opt-in regions like Hyderabad have to be added by hand.
A one-day window in a quiet account took a few minutes. The request survives you navigating away, so there is no need to sit on the page.
Section 06What it found
Nine ec2:Describe* calls and route53resolver:ListFirewallRuleGroupAssociations. That is the whole footprint. A user holding AdministratorAccess spent the window clicking around the VPC console, and every action it touched is read-only.
This is why I run the generator on long-lived human identities before I go anywhere near their policies. Whatever number I had in my head, it was not ten.
Section 07The policy it wrote
Two statements, and the split tells you something.
The first holds the nine actions that do not support resource-level permissions, so Resource is "*" and there is nothing to tighten. The second is ec2:DescribeVpcAttribute, which does support resource-level permissions, so instead of an answer you get a template:
"arn:aws:ec2:${Region}:${Account}:vpc/${VpcId}"
Red underline, error marker on line 23. You cannot create the policy until those placeholders become real values. I like that it blocks you. It forces one deliberate scoping decision instead of letting a wildcard slip through because you were clicking fast.
Section 08What it will not tell you
The generated policy is a first draft. Five things it leaves out:
- Anything outside the window. Quarter-end jobs, the annual certificate rotation, the break-glass path someone used once in March. A 1-day window like mine is close to useless for this, and 90 days is the ceiling.
- Data events. My trail logs management events only, so S3 object reads and Lambda invokes never reach the generated policy. Data events are a separate switch and a separate line on the bill.
- Conditions. No
aws:SourceIp, no MFA condition key, noaws:PrincipalOrgID. If you want those, you write them. - Denies. Everything comes back as
Allow. Guardrails stay in SCPs or a permissions boundary. - Intent. It reflects API calls that were logged, which is not the same as the permissions someone needs going forward. Console browsing throws off a pile of
Describenoise, and the generator writes that noise into the policy as though it were a requirement.
Section 09How I would actually run this
- Turn on a trail with validation and SSE-KMS, then leave it alone for 90 days before reviewing anything.
- Generate across every region the identity touches, opt-in regions included.
- Diff the result against what is attached today. That delta is a conversation with whoever owns the identity, not a ticket you close on your own.
- Replace the resource placeholders on purpose. Turning
${VpcId}into*is a decision, so write down why you made it. - Attach the policy, remove
AdministratorAccess, then watch the Last Accessed tab and Access Analyzer's unused access findings for a month before calling it done.
For this user, none of that is step one. Step one is MFA on the console login and deleting two access keys that are eleven days old and have never been used. A least-privilege policy on an identity with no MFA and two dangling long-lived keys is decoration.