Pull every assignment before you open a single profile

A permission audit that changes anything starts with an export, not with clicking through Setup. Before I open a single profile page I pull PermissionSetAssignment, PermissionSet, PermissionSetGroupComponent, ObjectPermissions and FieldPermissions into a spreadsheet, joined on user id. The point of that first hour is one row per person per object per right, so the org stops being forty profile pages and becomes something you can sort.

Setup shows you one profile at a time, and that view will mislead you by omission. Someone who looks harmless on the Standard User profile can sit in three permission set groups that hand back everything the profile withheld. In the last org I audited, a two hundred and forty person Sales Cloud tenant, there were thirty one profiles and two hundred and eighty six permission sets, and eleven of those sets had never been assigned to a single user.

Give the export the full first day. That file is the evidence you will argue from in week two, when a sales director says their team has always had that access and you need to show, with a date, that the grant came from a permission set built in March 2024 by someone who left in November.

Who actually holds the widest object permissions

The two flags worth checking before anything else are View All and Modify All, at the object level and at the org wide level. Modify All Data on a profile is easy to spot and usually already known to whoever owns the org. The per object versions hide inside permission sets nobody reads, and they survive every profile cleanup project that gets funded.

The finding that turns up in almost every audit I run is the project permission set that never got retired. On one engagement it was called Q4_Close_Support, created in October 2024 so a temporary revenue operations crew could fix stage and close date problems during a messy quarter end. It granted Modify All on Opportunity. Eleven people still had it when I ran the export fourteen months later, including two who had moved to marketing and one contractor whose user record was still active.

Permission set groups make this harder to see, since the group is what gets assigned and the grant lives one layer down in a component. Muting permission sets help, and they also mean a permission can be present and inactive at the same time, so read the muting set before you conclude anything. If you are about to point an AI agent at this org, whatever you leave in place becomes the boundary that agent inherits.

Check field level security on the fields that would embarrass you

Object access is the headline. Field level security is where the real exposure sits, and nobody reviews nine hundred custom fields in a week, so pick the ones that would cause a problem on a screenshot. In most Sales Cloud orgs that means the Opportunity discount and margin fields, anything on the quote line item carrying cost, contact records holding national identifiers, and the custom object the HR team built in the same org.

The pattern I see most is a field marked read only on the page layout and left fully visible in field level security. Page layout controls what the page renders. Field level security controls what the API, the report builder and the data export return. I watched a sales operations analyst build a report in eight minutes that pulled a custom cost to serve field across every open opportunity, a field the finance team believed four people could see.

Run FieldPermissions against your short list, count distinct users with read access, then compare that number to the list of people who are supposed to have it. If the gap is bigger than about ten people, the fix is a field level change rather than a set of individual conversations. Anyone who has worked through access requests that outlive their purpose will recognise the shape of it.

The integration users nobody has looked at since go live

Service accounts hold the widest access in most orgs and get reviewed the least, because no human is inconvenienced when they are over permissioned and every human is inconvenienced the moment you tighten them. Give them their own day. Pull every user on an API Only profile, every user whose name starts with svc or int or api, and every user referenced in your connected apps and named credentials.

The one I still think about ran under a full System Administrator profile because the middleware team hit an INSUFFICIENT_ACCESS_ON_CROSS_REFERENCE_ENTITY error at two in the morning during a go live weekend, and that was the fastest way to get the connection up. Three years later it was still running that way, still holding Modify All Data on everything, still authenticating with a password and security token that four people across two companies knew.

For each integration user, work out which objects it writes to, which fields it reads, whether it needs delete at all, and whether it ever logs in from outside the middleware IP range. Login History and the API request counter answer most of that in an afternoon. If nobody has mapped what each connection actually touches, the difference between a connector and an integration gets obvious fast.

Removing access without breaking someone's Monday

Finding overreach takes four days. Removing it takes the rest of the quarter, and that part decides whether the audit was worth running. My rule is that nothing gets removed without a named business owner who agreed to it, a scheduled date, a rollback that takes under an hour, and a line in the change record pointing at the export row that triggered it.

Sequence by who will notice. Start with assignments held by people who have not logged in for ninety days, because nobody will feel it and it proves the process works. Those users are usually an unused license as well as an open permission. Then take the retired project permission sets one at a time, with a note to those users a week ahead naming the set and a person to reply to. Do the integration users last, in a sandbox first, because that is the change that stops an order flow on a Tuesday afternoon.

Announce removals through whoever already runs change communication for the platform. A security email that reads as an accusation gets escalated instead of read. Set the rollback rule before you start, so a genuine blocker gets the access restored the same day and reviewed in the next cycle. Arguing with a revenue leader on a Monday morning is how permission audits get quietly cancelled in week three.

What the audit should leave behind

Most audits end with a findings document that gets read once and filed. What keeps working after you leave is a repeatable export and a naming rule. Every permission set created from now on carries a description that names the requester, states the reason, records a review date, and points at the change record behind it.

Re-run the same queries every quarter and diff them against the previous export. That diff is a fifteen minute job once the queries exist, which moves the audit from a week long project to a standing item on the platform team's agenda. Teams already running this cadence against security group sprawl in Workday adopt it fastest in Salesforce.

Put one question on the agenda for your next platform review. Ask who is allowed to create a permission set directly in production, and ask how many were created last quarter. In the org with the Q4_Close_Support set, the answer was nine admins and forty one new permission sets, and not one of them carried a review date.