Skip to main content
Guide for Salesforce admins

Salesforce access review: a step-by-step guide to who can see, export and delete everything

An auditor asks one question in many shapes: who can see all our data, who can change or delete it, and who can take it out? This is a step-by-step guide for the admin who has to answer. Each step has the Setup path, a check you run yourself, and what good looks like.

30 minute read · 8 steps · Queries you can paste · Facts checked 21 Oct 2026

Before you start

What an access review is, and what it is not.

An access review is a written answer to a plain question: who can do what in this org, and who agreed to it? You check the access, you decide if each person still needs it, and you sign the result with the date.

Salesforce says the customer owns this part. In its Well-Architected guidance: "You are responsible for security on the platform, including configuration, access controls, custom code, and data governance." (Salesforce Architects, checked 21 Oct 2026). Salesforce's own paper on SOX says user access should be "reviewed on a set cadence to ensure their access is still appropriate" (Salesforce SOX paper, checked 21 Oct 2026).

What this guide is not

It is not legal or audit advice. Salesforce's SOX paper says the same about itself. Ask your auditor which controls and which evidence they accept before you start. Then use this guide to do the work and keep the proof.

Many teams find this hard because the org is already messy. Salesforce Ben ran a 2026 survey about tech debt in general, not about access. It still shows the mood most admins start from.

Exhibit 1More than half of surveyed admins name tech debt as their biggest challenge

Source: Salesforce Ben, State of the Salesforce Admin Role in 2026 and Salesforce Ben, Mega 2026 Salesforce Admin Survey. Checked 21 Oct 2026.

Percent of respondents. More than 1,100 respondents in 72 countries. Third party, not Salesforce. Tech debt in general, not access.

How to use this guide

Go in order. Each step has the same five parts: what to do, where in Setup, a check you run yourself, what good looks like, and what can go wrong. The checks are SOQL queries (Salesforce's query language). You can paste them into the Developer Console Query Editor, into Workbench, or into the sf data query command.

On the Tooling API. If your query tool has a "Tooling API" option, leave it off. We ran every query in this guide in the standard mode, with no Tooling API option, against a Salesforce developer org on 21 Oct 2026. The counts in that org are test data, so we do not show them here. Your numbers are the point.

On the command line. Put the query on one line, inside quotes: sf data query -o your-org-alias -q "SELECT COUNT() FROM User WHERE IsActive = true". The Copy button on each query box copies it as one line.

Where the Setup paths come from. The Setup paths are for Lightning Experience. Type the Quick Find text into the Setup search box. Each path was taken from the Salesforce Help page named next to it.

Step 01

Decide the scope and who signs.

What to do

Write a one page scope before you run a single query. It answers five things.

  • Which org. Start with production. Add a sandbox if it holds real data.
  • Which kinds of access. Named users, profiles, permission sets, permission set groups and integration users (the accounts that other systems sign in with).
  • Which permissions. Salesforce's SOX paper names View All Data, Modify All Data and Modify Metadata as examples of permissions to put in scope (SOX paper). This guide adds export, delete and API permissions, because that is what the auditor's question covers.
  • The date. Pick the "as of" date. Run all the checks on that day, so the numbers agree with each other.
  • Who signs. A reviewer who does the check and an approver who accepts the result. Make them two different people, so nobody signs off their own access.

Salesforce's SOX paper also says that approval of changes to privileged access (roles, profiles and permission sets) is "a critical requirement of SOX compliance" (SOX paper, checked 21 Oct 2026). So the scope should say who approves a change, and where that approval is kept.

Where in Setup

Nothing to open yet. First check that you can see what you need to see. Salesforce lists these permissions for the pages and objects used below.

To seeYou needSource
The Users list in SetupView Setup and ConfigurationView and Manage Users
Who has which permission set (PermissionSetAssignment)View Setup and Configuration, Assign Permission Sets, or Manage UsersPermissionSetAssignment
Object permissions (ObjectPermissions)View Setup and ConfigurationObjectPermissions
Setup Audit TrailView Setup and ConfigurationSetup Audit Trail
Login HistoryManage Users or Monitor Login HistoryLoginHistory, Monitor Login History

Check it yourself

Count the active users. This number is the starting population for your scope page. Write it down with the date and time.

Query 1.1Active users, the starting population
SELECT COUNT() FROM User WHERE IsActive = true

Returns one number: the active users. A user with IsActive true "has access to log in" (User). If this query fails, you do not have enough access yet. Fix that before you promise the auditor a date.

What good looks like

A one page scope with the org, the "as of" date, the four kinds of access, the permissions in scope, and two names: reviewer and approver. Both have agreed to it.

What can go wrong

  • The scope leaves out integration users. They often hold more access than any person.
  • The reviewer is also a holder of the access being reviewed.
  • You run half the checks on Monday and half on Friday. Users change in between and the two halves do not match.

Step 02

List the active users and how they get access.

What to do

Every user gets access in up to three ways. One profile (you assign a profile to each user). Any number of permission sets, which add access on top of the profile. And permission set groups, which bundle permission sets for one job type. List the active users, then list how each one is covered.

Salesforce says permission sets "extend users' functional access without changing their profiles and are the recommended way to manage your users' permissions" (Permission Sets). A group "streamlines permissions assignment and management" (Permission Set Groups). For the profile, Salesforce says: "When possible, assign users the Minimum Access - Salesforce profile" and grant the rest with permission sets (Profiles).

Exhibit 2Four permissions skip sharing; Read, Create, Edit and Delete respect it

Profile

one profile per user

Permission sets

extend the profile

Permission set groups

bundle permission sets

Permissions

the user holds

Read, Create, Edit, Delete

Sharing settings

org-wide defaults and roles

Records the

user can reach

These four skip sharing

View All Data, Modify All Data, View All Records, Modify All Records

Source: Comparing Security Models and Factors that Affect Data Access, checked 21 Oct 2026.

A structural diagram. No numbers.

Salesforce's wording: "The 'Read,' 'Create,' 'Edit,' and 'Delete' permissions respect sharing settings", while "View All Data" and "Modify All Data" "override sharing settings for all objects" (Comparing Security Models). The per-object versions, View All Records and Modify All Records, do the same for one object. That is why steps 3 and 4 look at both kinds.

Profiles are not being retired on that dateSalesforce planned to end permissions on profiles in Spring '26, then cancelled that, in a Salesforce Help article published 6 June 2026. Profiles still hold permissions today. Salesforce still tells admins to use permission sets, with profiles for baseline settings (Salesforce Help, cancelled, checked 21 Oct 2026). The original plan is in a January 2023 post on the Salesforce Admins blog. Review profiles and permission sets both.

Where in Setup

  • Setup > UsersQuick Find: Users (source)
  • Setup > ProfilesQuick Find: Profiles (source)
  • Setup > Permission SetsQuick Find: Permission Sets (source)
  • Setup > Permission Set GroupsQuick Find: Permission Set Groups (source)
  • Setup > Sharing SettingsQuick Find: Sharing Settings, then Edit in the Organization-Wide Defaults area (source)
  • Setup > RolesQuick Find: Roles (source)

Check it yourself

Query 2.1Active users by profile
SELECT Profile.Name, COUNT(Id) c
FROM User
WHERE IsActive = true
GROUP BY Profile.Name

Returns one row per profile with the number of active users on it. In our test org one group came back with no profile name. We did not find out why, so treat a blank as a thing to look into, not as an answer.

Query 2.2Active users by user type
SELECT UserType, COUNT(Id) c
FROM User
WHERE IsActive = true
GROUP BY UserType

Returns one row per kind of user (Standard users, portal users, guest users and others, depending on your licences). It shows early if you have kinds of user you forgot to put in scope.

Query 2.3Active users per permission set (not counting profile-owned sets)
SELECT PermissionSet.Name, COUNT(Id) c
FROM PermissionSetAssignment
WHERE Assignee.IsActive = true
AND PermissionSet.IsOwnedByProfile = false
GROUP BY PermissionSet.Name

Returns one row per permission set that is assigned to an active user, with the user count. Salesforce marks a permission set that belongs to a profile: "If true, the permission set is owned by a profile" (PermissionSet). This query leaves those out, so you see only the sets people were given on top of a profile. If you use permission set groups, also open them in Setup > Permission Set Groups.

What good looks like

Each active user maps to one profile. Most extra access arrives through named permission sets or groups, not through a custom profile per person. Each set has an owner who can say what it is for.

What can go wrong

  • A profile-only review. It misses everything granted by permission sets. A permission-set-only review misses the profiles. Steps 3 to 5 cover both, because both live in PermissionSetAssignment.
  • Counting users twice. A user with two permission sets is two rows. Count distinct users when you need a headcount.
  • Inactive users are left out on purpose. The queries filter on IsActive = true. If the auditor asks about people who left, check that their access was removed when they were deactivated.

Step 03

Find who can see everything.

What to do

Two permissions let a person read records they would not normally see. View All Data works across the whole org: "Users can view all records regardless of sharing settings" (Factors that Affect Data Access). View All Records does the same for one object, for example Account (ObjectPermissions). Find every person who holds either.

Modify All Data also switches on View All Data and View All Records on all objects (Modify All Data article). So the people from step 4 will show up here too. That is expected.

Where in Setup

  • Setup > Permission SetsOpen a set and look under App Permissions or System Permissions for the permission descriptions (source). Quick Find: Permission Sets
  • Setup > ProfilesQuick Find: Profiles. A profile's own permissions are also read by query 3.1

Opening each one by hand does not scale. Use the queries.

Check it yourself

Query 3.1Permission sets and profiles that carry View All Data
SELECT Id, Name, IsOwnedByProfile, PermissionsViewAllData
FROM PermissionSet
WHERE PermissionsViewAllData = true

Returns every permission set that has View All Data switched on. A row with IsOwnedByProfile true is a profile's own permissions, so the source is a profile (PermissionSet).

Query 3.2The people behind it
SELECT Assignee.Name, Assignee.Profile.Name, PermissionSet.Name, PermissionSet.IsOwnedByProfile
FROM PermissionSetAssignment
WHERE PermissionSet.PermissionsViewAllData = true
AND Assignee.IsActive = true

Returns one row per person per source: the active user, their profile, and the permission set that gave them View All Data. A person appears once for each set that grants it (PermissionSetAssignment).

Query 3.3How many different people
SELECT COUNT_DISTINCT(AssigneeId) c
FROM PermissionSetAssignment
WHERE PermissionSet.PermissionsViewAllData = true
AND Assignee.IsActive = true

Returns one number: the distinct active users who can see every record. This is the number to write on the review.

Query 3.4View All Records on one object (change Account to your own objects)
SELECT Parent.Name, Parent.IsOwnedByProfile, SobjectType
FROM ObjectPermissions
WHERE SobjectType = 'Account'
AND PermissionsViewAllRecords = true

Returns one row per permission set that lets its users "view all records for this object, regardless of sharing settings" (ObjectPermissions). Run it for the objects that matter most: the ones with customer, finance or health data. Add AND Parent.IsOwnedByProfile = false to hide profile-owned rows if the list is long.

The permission set group extra query

Salesforce's PermissionSet reference says the field PermissionSetGroupId returns the group id when a permission set is owned by a group (PermissionSet). We could not confirm from Salesforce's pages, or from our test org, that a group's combined permissions show up as flags on that group's own row. So the queries above might miss a person who gets View All Data only through a group. Run this second query too.

Query 3.5People who get View All Data or Modify All Data through a group
SELECT Assignee.Name, PermissionSetGroup.DeveloperName
FROM PermissionSetAssignment
WHERE PermissionSetGroupId IN (
  SELECT PermissionSetGroupId
  FROM PermissionSetGroupComponent
  WHERE PermissionSet.PermissionsModifyAllData = true
  OR PermissionSet.PermissionsViewAllData = true)
AND Assignee.IsActive = true

Returns the active users who hold a group that contains a permission set with Modify All Data or View All Data. Zero rows is a valid answer. In our test org it returned none, so we could not show it catching anything. It looks inside each group's member sets instead of trusting the group's own row.

What good looks like

A short list you can read out loud. Each name has a reason written next to it and an owner who agrees. People who only need to read should not hold it.

What can go wrong

  • Counting rows, not people. Query 3.2 lists a person once per source. Use 3.3 for the headcount.
  • Missing objects. View All Records and Modify All Records are "not available for ideas, price books, article types, and products" (Comparing Security Models). An empty result for those objects is not a clean bill.
  • No access to read the object. ObjectPermissions needs View Setup and Configuration (ObjectPermissions).
  • Groups. See the extra query above. Run both.

Step 04

Find who can change or delete everything.

What to do

This is the step with the highest stakes. Check these permissions, one by one.

  • Modify All Data. "Grants a user the highest level of data access, overriding all sharing rules and object-level permissions" (Modify All Data article). It also turns on a set of dependent permissions: read, create, edit, delete and view all records on all objects, plus Transfer Record and others. It opens the mass delete page in Setup and lets the user edit shares on objects they do not own (same article).
  • Modify All Records on one object. Lets users edit all records of that object, regardless of sharing. It needs Read, Delete, Edit and View All Records on the same object (ObjectPermissions).
  • Delete on an object. This one respects sharing settings (Comparing Security Models). It is still worth listing for your key objects.
  • Bulk API Hard Delete. It "allows users to permanently delete records using the Bulk API (bulk delete operations), bypassing the Salesforce Recycle Bin" (Bulk API Hard Delete article).
  • Others that change things: Transfer Record (change the owner of most records), Manage Users (create, edit and deactivate users and manage profiles and roles), Author Apex (create and edit Apex code), Customize Application (change the org with the App Setup menu), and Manage Encryption Keys (the Shield Platform Encryption key material). Each is in the table at the end (User Profile permission descriptions, Key Management and Rotation).

Two things people get wrong

Modify All Data is not Bulk API Hard Delete. The list of permissions that Modify All Data switches on does not include it (Modify All Data article). And the standard System Administrator profile does not carry Bulk API Hard Delete. Salesforce says it cannot be added to that profile directly. It is given through a cloned profile or a permission set (Salesforce Help, checked 21 Oct 2026). So check it on its own.

Where in Setup

  • Setup > ProfilesQuick Find: Profiles
  • Setup > Permission SetsQuick Find: Permission Sets
  • Setup > Permission Set GroupsQuick Find: Permission Set Groups

Check it yourself

Query 4.1Every permission set that carries a high-power permission
SELECT Id, Name, IsOwnedByProfile, PermissionsModifyAllData, PermissionsViewAllData, PermissionsBulkApiHardDelete, PermissionsDataExport, PermissionsAuthorApex, PermissionsManageUsers, PermissionsCustomizeApplication
FROM PermissionSet
WHERE PermissionsModifyAllData = true
OR PermissionsViewAllData = true
OR PermissionsBulkApiHardDelete = true
OR PermissionsDataExport = true
OR PermissionsAuthorApex = true
OR PermissionsManageUsers = true
OR PermissionsCustomizeApplication = true

Returns one row per permission set (and per profile, where IsOwnedByProfile is true) with a true or false for each permission. This is the overview page of the review. Paste it into a sheet.

Query 4.2Who holds Modify All Data, and when they last logged in
SELECT Assignee.Name, Assignee.Profile.Name, PermissionSet.Name, PermissionSet.IsOwnedByProfile, Assignee.LastLoginDate
FROM PermissionSetAssignment
WHERE PermissionSet.PermissionsModifyAllData = true
AND Assignee.IsActive = true

Returns one row per person per source. The profile-owned rows tell you the person got it from their profile. Rows for normal sets mean someone assigned a permission set on purpose.

Query 4.3How many different people hold Modify All Data
SELECT COUNT_DISTINCT(AssigneeId) c
FROM PermissionSetAssignment
WHERE PermissionSet.PermissionsModifyAllData = true
AND Assignee.IsActive = true

Returns one number. It is the answer to "how many users hold Modify All Data?" Write it down with the date.

Query 4.4Who can delete permanently (Bulk API Hard Delete)
SELECT Assignee.Name, Assignee.Profile.Name, PermissionSet.Name, PermissionSet.IsOwnedByProfile
FROM PermissionSetAssignment
WHERE PermissionSet.PermissionsBulkApiHardDelete = true
AND Assignee.IsActive = true

Returns the active users who can permanently delete through the Bulk API. An empty result is a fine result. Write "none" and the date on the review.

Query 4.5Modify All Records on one object (swap in PermissionsDelete to list Delete)
SELECT Parent.Name, Parent.IsOwnedByProfile, SobjectType
FROM ObjectPermissions
WHERE SobjectType = 'Account'
AND PermissionsModifyAllRecords = true
AND Parent.IsOwnedByProfile = false

Returns the normal permission sets that give Modify All Records on Account. Anyone with Modify All Data gets Modify All Records on every object, so their profile-owned rows would fill the list (Modify All Data article). We hide them here, because step 4.2 already lists those people. Remove the last line to see all of it.

Query 4.6Modify All Records, by object, outside profiles
SELECT SobjectType, COUNT(Id) c
FROM ObjectPermissions
WHERE PermissionsModifyAllRecords = true
AND Parent.IsOwnedByProfile = false
GROUP BY SobjectType

Returns one row per object that has at least one permission set with Modify All Records, and how many sets. Work down the list and ask who holds each.

To check another permission, swap the field name in query 3.2 or 4.4. Use the field names in the table at the end of this guide. One trap: the field for Transfer Record is PermissionsTransferAnyEntity. The name PermissionsTransferAnyRecord returns an error ("No such column").

What good looks like

Modify All Data: a short list of named admins, each with a reason, and none of them an integration user unless the integration truly needs it. Bulk API Hard Delete: nobody, or one named owner with a written reason. Modify All Records: on only the objects where someone can say why.

What can go wrong

  • Only checking the System Administrator profile. Custom profiles can carry Modify All Data, and so can permission sets. Query 4.1 finds all of them.
  • Trusting a profile name. A cloned or renamed profile can still hold the permission. The field is what counts, not the name.
  • A field name that is wrong. A misspelled permission field fails loud with an error. Do not read a failed query as "no one holds it".

Step 05

Find who can export.

What to do

Data leaves Salesforce in a few ways. Check each permission that Salesforce documents for it.

  • Export Reports. "Allows the user to use Export Details and Printable View to export reports" (User Profile permission descriptions).
  • Weekly Data Export. This is a different permission with a different job. Setup and Help call it "Weekly Data Export". It lets a user run the weekly or monthly data export, which is the whole org as CSV files. By default only the System Administrator profile has it (Export Backup Data from Salesforce).
  • API Enabled. "Allows the user execute API calls" (User Profile permission descriptions). An API call can read data, so treat it as a route out. Api Only User is a separate permission that limits a person to the API instead of the screen.
  • Run Reports. "Allows the user to view the reports tab, run reports, view dashboards based on reports." It is about running and viewing. Taking data out is the separate Export Reports permission (User Profile permission descriptions).

Be honest with the auditor about the limits

The row limit. Salesforce says you can export an unlimited number of rows and columns for a report in Details Only (Classic) or as XLS or CSV (Lightning) (Export a Report). So the limit is not a row cap. It is the permission.

Export Reports has no Setup page. It is a permission, not a page. Salesforce says to open a permission set and look under App Permissions or System Permissions for the permission descriptions (User Permissions). That page does not say which of the two lists holds Export Reports. Use the queries below.

What this guide does not cover. We did not check which logs show who actually exported what, so we do not promise that here. We also did not check every other way data can leave, such as third-party apps. This step covers the permissions Salesforce documents. Ask your auditor what else is in scope.

Where in Setup

  • Setup > Data ExportQuick Find: Data Export. Then Export Now or Schedule Export. Weekly export is for Enterprise, Performance and Unlimited editions. Monthly is for all editions except Database.com (source)
  • Setup > Permission SetsFor Export Reports, API Enabled and Run Reports. Quick Find: Permission Sets (source)

Check it yourself

Query 5.1Who can export reports
SELECT Assignee.Name, Assignee.Profile.Name, PermissionSet.Name, PermissionSet.IsOwnedByProfile
FROM PermissionSetAssignment
WHERE PermissionSet.PermissionsExportReport = true
AND Assignee.IsActive = true

Returns one row per person per source. The list is often long, because many report users need export. Group it by profile or by role and ask each group owner if the need is real.

Query 5.2Who can run the Weekly Data Export
SELECT Assignee.Name, Assignee.Profile.Name, PermissionSet.Name, PermissionSet.IsOwnedByProfile
FROM PermissionSetAssignment
WHERE PermissionSet.PermissionsDataExport = true
AND Assignee.IsActive = true

Returns the active users who can run the whole-org export. Salesforce says these users "can view all exported data and all custom objects and fields" (Export Backup Data from Salesforce).

Query 5.3API Enabled, counted by profile
SELECT Assignee.Profile.Name, COUNT_DISTINCT(AssigneeId) c
FROM PermissionSetAssignment
WHERE PermissionSet.PermissionsApiEnabled = true
AND Assignee.IsActive = true
GROUP BY Assignee.Profile.Name

Returns the distinct active users with API Enabled, per profile. Match it against your list of integrations in step 6. A named person with API Enabled and no reason is a finding.

What good looks like

Weekly Data Export: a very short list, kept to the roles that run backups. Export Reports: held by roles that need to take data out of the system, with the group owner's name. API Enabled: every holder is a named person with a reason, or a known integration.

What can go wrong

  • Mixing up the two export permissions. Export Reports is about reports. Weekly Data Export is about the whole org.
  • Assuming the default. Weekly Data Export starts with the System Administrator profile only. Someone may have added it since. The query tells you.
  • Promising a log you have not seen. Do not tell the auditor that every export is logged until you have found the log and read one entry.

Step 06

Check the stale and integration users.

What to do

Two groups of accounts quietly keep access: people who stopped using the org, and the accounts that other systems use. List both. Then ask who owns each one.

For "stale", pick a window and write it down. We use 90 days in the queries as an example. Your policy or your auditor sets yours. LastLoginDate is "updated if 60 seconds elapses since the user's last login" (User), so it is good enough for a window of weeks.

Where in Setup

  • Setup > UsersQuick Find: Users (source)
  • Setup > Login HistoryQuick Find: Login History (source)
  • Setup > ProfilesTo see which profiles are limited to the API. Quick Find: Profiles

Check it yourself

Query 6.1Active standard users with no login in 90 days, or never
SELECT Name, Username, Profile.Name, LastLoginDate
FROM User
WHERE IsActive = true
AND UserType = 'Standard'
AND (LastLoginDate < LAST_N_DAYS:90 OR LastLoginDate = null)
ORDER BY LastLoginDate

Returns the active, normal users who have not logged in for 90 days or have never logged in. Each one is either still needed, or should be deactivated. A row with a blank last login is someone who got a seat and never used it.

Query 6.2How many have never logged in
SELECT COUNT()
FROM User
WHERE IsActive = true
AND UserType = 'Standard'
AND LastLoginDate = null

Returns one number. It is a quick sign of how well joiners and leavers are handled.

Query 6.3High access plus stale: Modify All Data holders who have not logged in
SELECT Assignee.Name, Assignee.LastLoginDate, PermissionSet.Name
FROM PermissionSetAssignment
WHERE PermissionSet.PermissionsModifyAllData = true
AND Assignee.IsActive = true
AND (Assignee.LastLoginDate < LAST_N_DAYS:90 OR Assignee.LastLoginDate = null)

Returns the most urgent rows in the whole review: people with the highest access who do not use it. Change the permission field to run the same check for View All Data, Bulk API Hard Delete or Weekly Data Export.

Query 6.4Integration users: active users on a profile limited to the API
SELECT Name, Profile.Name, LastLoginDate
FROM User
WHERE IsActive = true
AND UserType = 'Standard'
AND Profile.PermissionsApiUserOnly = true

Returns the normal users whose profile is limited to the API. These are often integration accounts. Salesforce's Profile reference says each permission is a field named Permissions plus the permission name (Profile). It does not list this one, so we confirmed the exact name by running the query in a developer org. The permission itself is documented: "Allows the user to access Salesforce through API only instead of the user interface" (User Profile permission descriptions).

Query 6.5Which profiles are limited to the API
SELECT Id, Name
FROM Profile
WHERE PermissionsApiUserOnly = true

Returns the profile names. Do not search for a profile called "API Only User". In our test org that name filter returned nothing, while this flag filter found the profiles. Also look at the user types from query 2.2: integration and automated process users can be a separate type from Standard.

What good looks like

Every stale user is deactivated, or has a written reason to stay. Every integration user has a named owner, a purpose, the least access it needs, and a date to review. Nobody uses a shared login that could be a named person.

What can go wrong

  • Integrations hold more than they need. An integration that was built fast may run as a full admin. Check query 6.4 users against steps 3 and 4.
  • The window is too long or never written down. The auditor will ask what "stale" meant.
  • A person who is on leave looks stale. That is a reason to record, not to ignore.

Step 07

Read the evidence trail.

What to do

The review says what access exists today. The trail shows how it changed and who approved it. Salesforce's SOX paper points to Setup Audit Trail for admin changes, and says to extract the records if you need them longer than 180 days (SOX paper). Know what each tool keeps, and for how long.

EvidenceWhat it keepsSource
Setup Audit Trail"Setup Audit Trail records are retained for 180 days." The Setup page shows only the 20 most recent changes. Download the file for more.SOX paper, Setup Audit Trail, SetupAuditTrail
Login HistorySix months online, or ten years with Event Monitoring. Download as CSV or GZIP. "The All Logins option includes API access logins."SOX paper, Monitor Login History
Field History TrackingUp to 18 months in the user interface and up to 24 months through the API. With Field Audit Trail, until you delete it. User field history (a Beta setting) can track Profile and Role changes, up to 20 fields. A role change is tracked only when you edit the user record, not when you assign it on the Roles Setup page.Field History Tracking Overview, Field Audit Trail
Event MonitoringEvent log files, read in the Event Log File Browser. It needs the View Event Log Files and View Setup permissions.Event Log File Browser

Where in Setup

  • Setup > View Setup Audit TrailQuick Find: View Setup Audit Trail (source)
  • Setup > Login HistoryQuick Find: Login History (source)
  • Setup > Field History TrackingQuick Find: Field History Tracking (source). Field Audit Trail has no Quick Find line on its Help page. Turn on Field History Tracking first, and note that Field Audit Trail needs "the purchase of Salesforce Shield or Field Audit Trail licenses" (source)
  • Setup > Event Log File BrowserQuick Find: Event Log File Browser (release note, Help page)
  • Setup > Data ExportNot the evidence tools, but the place to save a copy. Quick Find: Data Export

Check it yourself

Query 7.1How many setup changes in the last 30 days
SELECT COUNT()
FROM SetupAuditTrail
WHERE CreatedDate = LAST_N_DAYS:30

Returns one number. Use COUNT() and not COUNT(Id). Salesforce's reference says the first works and the second fails on this object (SetupAuditTrail). We got the same error.

Query 7.2Permission set changes in the last 30 days
SELECT CreatedDate, CreatedBy.Name, Action, Section, Display
FROM SetupAuditTrail
WHERE CreatedDate = LAST_N_DAYS:30
AND Action LIKE 'permSet%'
ORDER BY CreatedDate DESC

Returns who created, assigned, changed or removed permission sets and groups, and when. Action values we saw include PermSetAssign, PermSetUnassign, PermSetCreate, PermSetEnableUserPerm and PermSetGroupUserPermEnabled. Use the LIKE form. A list of exact action names missed rows when we tried one.

Query 7.3Profile changes in the last 90 days
SELECT CreatedDate, CreatedBy.Name, Action, Section, Display
FROM SetupAuditTrail
WHERE CreatedDate = LAST_N_DAYS:90
AND (Action LIKE 'profile%' OR Action = 'deletedprofile')
ORDER BY CreatedDate DESC

Returns profile changes. Action values we saw include profilePermChangedCustom, profileFlsChangedStandard, profileFlsChangedCustom, profileClonedStandard and deletedprofile. The LIKE 'profile%' part misses deletedprofile, so the query asks for it by name. This list can be long. Narrow the date range, or scan it for the permission names from steps 3 to 5.

Query 7.4Users created and deactivated in the last 90 days
SELECT CreatedDate, CreatedBy.Name, Action, Section, Display
FROM SetupAuditTrail
WHERE CreatedDate = LAST_N_DAYS:90
AND Action IN ('createduser', 'deactivateduser')
ORDER BY CreatedDate DESC

Returns the joiners and leavers that admins made in Setup. Compare it with your HR leavers list.

Query 7.5Logins in the last 30 days, by type
SELECT LoginType, COUNT(Id) c
FROM LoginHistory
WHERE LoginTime = LAST_N_DAYS:30
GROUP BY LoginType

Returns how many logins of each login type happened in the window. LoginHistory "represents the login history for all successful and failed login attempts" (LoginHistory).

Query 7.6The latest 20 logins, with status
SELECT UserId, LoginTime, LoginType, Status, SourceIp
FROM LoginHistory
WHERE LoginTime = LAST_N_DAYS:7
ORDER BY LoginTime DESC
LIMIT 20

Returns recent logins with their status. You cannot filter by status in a query. Select it, then sort or count in your sheet, or download the file from Setup.

Two more traps. Filtering SetupAuditTrail on Section fails with "can not be filtered in a query call". Filter on Action or CreatedDate, and read Section in the SELECT list. And the login history object is readable "with one exception" only by users with Manage Users or Monitor Login History (LoginHistory).

What good looks like

For the review period, every change to privileged access in the audit trail matches an approval you can show. And you saved a copy of the trail before the 180 days ran out. For this check, pick the permission set changes from query 7.2 and trace three of them to an approval.

What can go wrong

  • The trail has aged out. After 180 days the Setup Audit Trail no longer has it. Save a copy on a schedule. Salesforce's SOX paper lists the API, Data Loader and Event Monitoring as ways to keep it longer (SOX paper).
  • Field history does not cover everything. It tracks the fields you turned on. Turn it on before you need it.
  • No approval trail. The audit trail says who made a change. It does not say who approved it. That record is yours to keep (step 8).

Step 08

Write it up for the auditor, and sign.

What to do

Now turn the lists into one record. For each person who holds high access, write one row with six facts.

  • User and their role (the job, not the Salesforce role).
  • Access: which permission, and how they got it (profile, permission set or group).
  • Reason: why this person needs it.
  • Approver and date.
  • Decision: keep, remove or change.

Attach the queries you ran, the "as of" date and time, and the saved output. If you removed access, run the check again afterwards and attach the "after" result too. The audit trail will then show your own change, with your name on it.

ExampleA finished review, made-up data

Acme Foods is a made-up company. Pat Example and the other names are made up. Use it as a shape, not as a result.

UserJob roleAccess foundReasonApprover, dateDecision
Pat ExampleSalesforce adminModify All Data, through the System Administrator profileRuns deployments and user changesSam Sample, Head of IT. 14 Oct 2026Keep
Robin SampleSales operationsExport Reports, through the Sales Ops permission setBuilds the monthly board packSam Sample, Head of IT. 14 Oct 2026Keep
Lee DummyFormer contractorView All Data, through the Reporting permission set. No login for 90 daysNone foundSam Sample, Head of IT. 15 Oct 2026Remove
Acme ERP sync (integration)Integration. Owner: Pat ExampleAPI Enabled and Modify All DataWrites orders. Needs edit on Order onlySam Sample, Head of IT. 15 Oct 2026Change

The sign-off under the table:

ReviewerPat Example
ApproverSam Sample
Date16 Oct 2026
Next review16 Jan 2027

Where in Setup

The write-up lives outside Salesforce, in your controls folder or ticket system. In Setup you only need the paths from the earlier steps, so you can show the auditor the screens: Users, Profiles, Permission Sets, Permission Set Groups, and View Setup Audit Trail.

How often

Salesforce's SOX paper says to review "on a set cadence" and does not give a number (SOX paper). Pick one your policy and your auditor accept, write it on the sign-off, and put the next date in a calendar. Also run the review when something big changes: a new integration, a merger, a large permission set change, or a person with high access leaving.

What good looks like

One document, dated, with every row filled in and two signatures. Someone who was not there can read it and see what you found, what you decided, and who agreed.

What can go wrong

  • The reason column says "needed for the job". Write the task.
  • The approver is the person with the access.
  • You keep the findings in a chat thread. Put them in the folder the auditor will look in.
  • You never set the next review date, so the review happens once.
What Clientell AI does for this

Steps 2 to 6 are a lot of queries and a lot of sheets. Clientell AI connects to your org, production or sandbox, and lists and maps what it finds: the users with high access, and the permission sets and profiles behind each one. It drafts any fix and shows it to you. Clientell AI asks before it changes anything in production. Sandboxes and deployments are free. You still write the reason, get the approval and sign.

Reference

The permissions in one table.

What each permission allows, where to check it, and the page it comes from. The check column gives the Setup list to open and the field to use in the queries (a permission set and a profile's own permissions use the same field names).

PermissionWhat it allowsWhere to checkSource
Modify All DataThe highest level of data access. Overrides all sharing rules and object permissions.Profiles or Permission Sets. Field PermissionsModifyAllDataModify All Data
View All DataView all records regardless of sharing settings.Profiles or Permission Sets. Field PermissionsViewAllDataData access factors, Descriptions
Modify All Records (per object)Edit all records of one object regardless of sharing. Needs Read, Delete, Edit and View All Records on that object.Object settings in a permission set. ObjectPermissions.PermissionsModifyAllRecordsObjectPermissions
View All Records (per object)View all records of one object regardless of sharing.Object settings in a permission set. ObjectPermissions.PermissionsViewAllRecordsObjectPermissions
Delete (per object)Delete records. Respects sharing.Object settings. ObjectPermissions.PermissionsDeleteSecurity models, ObjectPermissions
Bulk API Hard DeletePermanently delete records through the Bulk API, bypassing the Recycle Bin. Not on the standard System Administrator profile.Cloned profile or permission set. Field PermissionsBulkApiHardDeleteHard Delete
Transfer RecordChange the owner of most records.Profiles or Permission Sets. Field PermissionsTransferAnyEntityDescriptions
Export ReportsUse Export Details and Printable View to export reports.No Setup page. Open a permission set. Field PermissionsExportReportDescriptions, Export a Report
Weekly Data ExportRun the weekly or monthly data export. By default only the System Administrator profile has it.Setup > Data Export. Field PermissionsDataExportData export, Descriptions
API EnabledExecute API calls.Profiles or Permission Sets. Field PermissionsApiEnabledDescriptions
Api Only UserAccess Salesforce through the API only, not the user interface.Profiles. Field PermissionsApiUserOnly (name confirmed by running it; the Profile reference does not list it)Descriptions
Run ReportsView the reports tab, run reports, view dashboards based on reports.Profiles or Permission Sets. Field PermissionsRunReportsDescriptions
Manage UsersCreate, edit and deactivate users and manage security settings, including profiles and roles. Can also view login history.Profiles or Permission Sets. Field PermissionsManageUsersDescriptions, Users, Login history
Author ApexCreate and edit Apex code.Profiles or Permission Sets. Field PermissionsAuthorApexDescriptions
Customize ApplicationCustomize the org using the App Setup menu.Profiles or Permission Sets. Field PermissionsCustomizeApplicationDescriptions
View Setup and ConfigurationView the App Setup menu and Administrative Settings pages. Also needed to read many security objects through the API.Profiles or Permission Sets. Field PermissionsViewSetupDescriptions, ObjectPermissions, PermissionSetAssignment
View All UsersView all user objects regardless of sharing.Profiles or Permission Sets. Field PermissionsViewAllUsersDescriptions
Manage Encryption KeysManage Shield Platform Encryption key material. Needs a Shield licence.Profiles or Permission Sets. Field PermissionsManageEncryptionKeysKey rotation

All rows checked 21 Oct 2026. The field names were run in a developer org on that date. Field names start with Permissions and are read from the PermissionSet object (PermissionSet).

Take it with you

Get the checklist.

The checklist is the whole guide on two printed pages: one group for each step, with a pass line and an Ask line for each check, and boxes for the reviewer, the approver, the date and the next review.

Download the checklist

Work email only. A free-mail address is not accepted.

The checklist opens right here. We may send occasional Salesforce admin tips; unsubscribe anytime. Privacy policy

Run steps 2 to 6 on your own org.

Connect a sandbox or production org. Clientell AI lists and maps the users with high access and the permission sets behind them. 14-day free trial, no card. The trial starts when you connect your first org.

No card needed