Testing and defence led by a published security researcher with five CVEs, including a CVSS 9.1 Mirai botnet kill-switch.
All security →SOC 2, ISO, and the Canadian privacy stack, run end to end with an independent auditor.
All frameworks →Original research, free tools, and plain-language guides on security and compliance, from a published security researcher.
Read the blog →The most sampled control in both frameworks, and the one most often run in a spreadsheet. What the population has to be, what a reviewer has to actually decide, and what proves it happened.
An access review evidences three separate assertions, and an auditor tests each one on its own: that the population was complete, that a competent reviewer made a decision on every line, and that every decision requiring action was actioned and can be proven. Most reviews that fail do not fail on the first two. They fail because eleven accounts were marked for revocation and nobody can show they were revoked, which documents a decision the organization then did not act on. Get the population from the system of record with a row count, put a named owner on every decision, and close the campaign only when the outstanding revocations are zero.
The user access review is sampled in nearly every SOC 2 and ISO 27001 engagement we run, and it is sampled harder than most controls because it is one of the few that is supposed to produce a decision record rather than a configuration state. A firewall rule either exists or it does not. An access review is a human judgement about whether someone should still be able to reach production, and the auditor is testing whether that judgement was made, by someone qualified to make it, on every line.
It helps to stop thinking of the review as one control and start thinking of it as three assertions tested separately. The first is completeness of the population: the list you reviewed was every account on that system at that point in time, and you can show it was. The second is a real decision on every line: a named reviewer looked at each account and its entitlements and said keep, reduce, or revoke. The third is action on the decisions: the accounts marked for revocation were revoked, on a date, and there is an artifact from the system that proves it.
A campaign that nails the first two and misses the third is in a worse position than a company that never ran a review at all. A company with no review has a control design gap. A company with a completed review listing eleven accounts to revoke and no proof of revocation has produced written evidence that it identified inappropriate access and left it in place. Auditors read that exactly the way it sounds, and so do the regulators and insurers who eventually see the report.
This guide covers what to put in the record so that each of the three assertions is independently provable. It is deliberately more detailed than our post on the SOC 2 access review process, which covers the shape of the control. Here we are concerned with the artifacts.
Before you build a population of accounts you need a population of systems, and this is where scope errors get baked in for a whole audit period. The list is not "the tools we pay for". It is every system that stores, processes, or can reach in-scope data, plus every system that grants access to one of those.
For a typical SaaS company the reviewable set is smaller than the SaaS inventory and larger than people expect. The identity provider is on it because it grants everything else. The cloud console is on it. The production database and any direct query tool is on it. The code repository is on it, because a commit reaches production. The CI/CD system is on it for the same reason, and its secrets store doubly so. The ticketing and support tools are on it if agents can see customer records, which they almost always can. The observability stack is on it if logs contain customer data, and logs usually contain more than the team believes.
Systems you exclude need a written reason, recorded once and reused, not improvised during fieldwork. "Excluded: the marketing site CMS holds no customer data and has no path to the production VPC" is a defensible position. A blank is not. An auditor reads an unexplained absence as an omission, which is the same rule that governs the vendor register.
Reconcile this list against the asset inventory at least once a period. If a SaaS application appears in the inventory with a data classification of confidential and does not appear in the access review schedule, one of the two records is wrong. That reconciliation is a five-minute check that catches a category of finding auditors love.
The population is the set of accounts on one system at one moment. What the auditor tests is not who was on it, but whether it was everyone. The way you make that provable is to export directly from the system of record, keep the raw export exactly as it came out, and record three things alongside it: the date and time the export was taken, the row count, and how it was produced.
How it was produced matters more than it sounds. "Exported from the AWS IAM console, Users tab, all users, 2026-09-04 09:12 UTC, 47 rows" is re-derivable. A spreadsheet somebody assembled by hand is not, and an auditor cannot distinguish an assembled sheet from an edited one. Where the system offers an API or a CLI, record the command. Where it does not, take the export and a screenshot of the row count in the same session.
The most frequent completeness failure is the identity provider blind spot. Teams pull the user list from the IdP and treat it as the population for every application behind single sign-on. It is not. Local accounts that bypass SSO do not appear in the IdP, and they are exactly the accounts that outlive a departure: the shared vendor login, the contractor account created before SSO was enforced, the admin account created during the initial setup of the tool. Every application behind SSO needs its own user export from its own admin console at least once a period, precisely to find those.
The second failure is filtered exports. Many consoles default to showing active users only. Disabled, suspended, invited-but-never-accepted, and licensed-but-dormant accounts are all still accounts, and a disabled account that can be re-enabled by anyone with an admin seat is a live risk. Export everything and let the review decide, rather than letting the export decide.
Split the population into sub-populations before you send it out. Internal staff, contractors, service and machine accounts, external or customer-facing support access, and break-glass accounts each get reviewed against different questions and often by different reviewers. Mixing them produces a review where the reviewer approves forty rows without reading, because thirty-six of them are obviously fine.
The access review is a detective control. It finds access that should not exist. The joiner, mover and leaver process is the preventive control that is supposed to stop it existing. Auditors test both, and they reconcile one against the other, because a review that finds nothing every quarter either means the preventive control works or means nobody is looking.
The authoritative population for people is the HR roster, not the identity provider. Ask HR for a roster as at the period end with start dates, end dates, employment type, and manager. That single file is the core population that everything else samples from, and it is one of the first things we request at kickoff on any engagement, before any control work starts. Reconcile it to the IdP: everyone on the roster should have an account, and everyone with an account should be on the roster or explained as a contractor or a service account.
Joiners are the easy half. The evidence is a request and an approval that predate the account creation, and an account creation timestamp. If the account was created on the 3rd and approved on the 9th, that is an exception whether or not the person was entitled to it, because the control being tested is that access is approved before it is granted.
Leavers are the half that produces findings, and the evidence is a chain: a termination record with a date, then a deactivation artifact from each in-scope system with its own timestamp, then the elapsed time between the two measured against whatever your policy promised. If your access control policy says access is revoked within one business day and one leaver took nine days in the code repository, you have an exception. Write the policy commitment you can actually hit. A policy that says one business day and a process that hits three is a self-inflicted finding, and changing the policy to three is cheaper than missing it four times a year.
Movers are the weakest link in almost every program, and the one auditors have learned to probe. A support agent promoted to engineering keeps the support tool admin role and gains the repository. Nothing was provisioned incorrectly at any single moment, and the aggregate is a person with far more reach than their role requires. The fix is that a role change triggers the same entitlement review as a departure, and the record of it is a dated ticket showing what was added and what was removed. Accumulated entitlement from internal moves is one of the standard forms of control drift, and it is invisible until somebody looks at one person across all systems rather than one system across all people.
Sending a spreadsheet to a manager and receiving no reply is not a review. Silence is not a decision, and an auditor will say so. Approval by absence of objection fails on the same principle that an unfilled column fails: there is no record that anybody exercised judgement.
The reviewer has to be someone positioned to know. That is the system owner or the line manager, not whoever runs compliance. The compliance owner runs the campaign, chases it, and files the record. They cannot be the person who decides whether a given engineer still needs write access to the production bucket, because they do not know, and an auditor who sees one name against all 180 decisions will ask how that person had the knowledge to make them.
The reviewer also has to be looking at entitlements, not account names. A line that reads "[email protected], active" is not reviewable. A line that reads "Jane Okafor, [email protected], role: AdministratorAccess on the production account, last sign-in 2026-08-28, granted 2024-03-11" is reviewable, because a manager can look at it and know immediately whether it is right. Enriching the export with role or group, last activity, and grant date is the single change that most improves the quality of the decisions you get back.
Last-activity data is worth chasing even when it is awkward to obtain. An account with no sign-in for 200 days is either dormant and should go, or is a break-glass account that needs a different kind of justification. Either way the reviewer needs to see it. Dormancy is also what turns a review from a rubber stamp into something that finds things, which is what you want, because a review that never finds anything is a review an auditor will not believe.
Four decision states cover everything, and the fourth is the one that matters for control quality. Keep means still required for the role. Reduce means keep the account, drop some of what it can reach. Revoke means no longer required, which obliges an action and a confirmation. Not decided means nobody has said whether this access is still needed, and any line still sitting in that state when the campaign closes makes the review incomplete for that line.
Reduce is the state most spreadsheet reviews lack, and its absence quietly degrades every review that runs without it. Faced with keep or revoke, a manager looking at an engineer who needs read access but currently has write will pick keep, because revoke is wrong and there is no third option. Over four quarters that is how an entire team ends up with production write access nobody ever explicitly granted.
The complete record for one campaign should let a reader who was not there reconstruct exactly what happened. That means:
This is the half of the control that gets skipped, and it is the half that costs the most when it is skipped. The decision record proves you looked. The completion record proves you acted. Only the second one closes the loop, and only the second one is missing in most of the reviews we are handed at the start of a remediation engagement.
What counts as proof of revocation is an artifact from the system itself, not an assertion in the tracker. Acceptable forms are an audit log entry showing the deactivation with a timestamp and the actor, a post-revocation export of the same population showing the account absent or disabled, or a ticket that carries both the request and the system confirmation. What is not acceptable is a tick in a column with no supporting artifact, because that is the compliance owner asserting the engineer did the work, which is the thing under test.
Record the elapsed time between the decision and the action, because your own policy probably sets a window and the auditor will measure against it. If nine revocations were decided on 1 October and six were actioned within two business days and three took eleven, the three are exceptions, and the useful thing to do is note them, open a corrective action about the handoff between the reviewer and whoever executes revocations, and show the next campaign closing faster. An exception with a corrective action attached and evidence of improvement reads very differently from an exception found by the auditor.
Do not close a campaign with outstanding revocations. If two of them are genuinely blocked, for example an account that cannot be removed until a contract ends, record that as a documented risk acceptance with an owner and a date, not as an open row. An accepted risk with a name on it is a governance decision. An open row is an unfinished control.
Privileged access is reviewed separately, more often, and to a higher standard, and the first thing you need is a written definition of what privileged means in your environment. Without one, every discussion becomes a debate about whether a given role counts. A workable definition names the specific roles: cloud account root and administrator equivalents, production database write, repository administrator and branch protection bypass, CI/CD secrets access, identity provider administrator, and anyone who can grant any of those to anyone else.
That last category is the one people miss. The ability to grant privilege is privilege. A user with permission to modify IAM policies is a production administrator with an extra step, and treating them as a standard user because their current policy is narrow is a design flaw an auditor will find by reading the policy document rather than the user list.
For every privileged account the record should hold a business justification tied to a named role, the date it was approved and by whom, whether the access is standing or granted just in time, and evidence of monitoring. If you use just-in-time elevation, the elevation log becomes strong evidence and typically lets you argue for a lighter standing-access population, which is a real reduction in both risk and audit surface.
Break-glass accounts need their own record: where the credential lives, who can retrieve it, what alerting fires when it is used, when it was last used and why, and when the retrieval path was last tested. An untested break-glass path is not a control, and an auditor asking about disaster recovery and an auditor asking about privileged access will both arrive at the same question.
The population nearly everyone forgets is the one that is growing fastest. API keys, CI tokens, personal access tokens, machine users, OAuth applications granted into your workspace, long-lived cloud access keys, and now agent and automation credentials. These are accounts with entitlements, and most of them were created by an engineer solving a problem on a Thursday and never revisited.
The field that makes a non-human identity reviewable is the same field that makes an asset reviewable: the owner. A named human has to be accountable for each one. Without an owner there is nobody to ask whether it is still needed, what it is used for, or whether its scope is still appropriate, and the default answer becomes keep, forever. The asset inventory has exactly this property, which is why the two registers reinforce each other and why an unowned row in either one is the highest-value thing to fix.
The per-identity record should carry the owner, the purpose in one sentence, the scope or permissions granted, the creation date, the last-used date if the platform exposes it, and the rotation policy or expiry. Reviewing these is not the same question as reviewing a person. The question is not "does this person still work here", it is "does this integration still exist, does it still need this scope, and when was this credential last rotated".
Third-party OAuth grants deserve a specific mention because they sit outside the usual account list entirely. An application a staff member authorised against your Google Workspace or Microsoft tenant three years ago may still hold a token with broad mailbox or drive scope. The tenant admin console lists them. Reviewing that list once a period, with a decision on each, is a short exercise that regularly finds something, and it maps cleanly onto the same review record you already keep.
Neither SOC 2 nor ISO 27001 hands you a frequency. Both work the same way: you document the interval, and then you are held to the interval you documented. This surprises teams who go looking for a rule and find none, and it catches teams who wrote quarterly into a policy template without noticing they had committed to four campaigns a year across every in-scope system.
Quarterly is the commitment most policies make and the one most enterprise questionnaires expect to see. Semiannual is defensible for a small, stable, low-change system with a handful of users, provided the policy says so and the risk assessment supports it. Annual is thin for anything privileged and will attract questions. Privileged access is commonly reviewed quarterly or monthly regardless of what the general population gets.
The expensive mistake is the mismatch. A policy that says quarterly and a program that ran two campaigns in twelve months is a self-reported control failure, and it is worse than a policy that honestly said semiannual and delivered two. Before an observation window opens, read your own policies and make the stated cadence match what you will genuinely operate. That reconciliation is one of the cheapest pieces of pre-window work available, and we treat it as part of fitting the policy set to the systems actually run rather than as a documentation task.
The reason the cadence has to be right before the window opens rather than during it is that an access review is a contemporaneous record. It only exists if it was made at the time. A review dated last quarter cannot be run today, and no amount of good faith produces one. If a quarter inside a Type II observation window has no review in it, that period is permanently unevidenced, and the only remaining options are a shorter window or a qualified opinion. We have written about the mechanics of holding a period together in operating a Type II observation window, and the observation window planner will lay out what has to land in each month before you commit to dates.
Understanding the sampling mechanics changes how you prepare, because it tells you what has to be re-derivable rather than merely present.
For a control that operates on a schedule, the auditor first establishes how many times it should have operated in the period, then selects occurrences to test. For a quarterly control across a twelve-month window there are four occurrences and the auditor commonly tests two. For a monthly control there are twelve and they commonly test two to four. Sample sizes vary by firm and by assessed risk, and a control with a prior-year exception attracts a larger sample. Within a selected campaign, the auditor then samples individual rows from the population, often five to twenty-five depending on population size, and traces each one to its decision and its action.
The property that makes your own internal sampling defensible is that it can be re-derived. Record the population size, the selection method, and the seed or the rule you used, so that the same population and the same seed always produce the same selection. "We picked a few" is not a method. A recorded population size with a seeded selection is, and it is the difference between a sample the auditor accepts and one they replace with their own, which always costs more time.
Expect reperformance on this control more than most. The auditor may pull the current user list themselves and compare it to your reviewed population, looking for accounts that exist now and were never on any reviewed list, and for accounts you marked revoked that are still active. Both findings are common and both are avoidable by running the same comparison yourself before you hand anything over. Doing that comparison is exactly what the evidence QA step in a remediation engagement is for: checking each artifact against what the audit firm will accept, before the audit firm sees it.
These are the recurring reasons an access review does not survive testing. Most of them are cheap to fix in advance and expensive to fix during fieldwork.
The first campaign is the one that reveals how bad the underlying data is, so run it early and expect it to be slow. Later campaigns take a fraction of the time because the populations, the reviewer assignments, and the export procedures already exist.
Days one and two: fix the system list and the reviewer for each. Get the HR roster as at the period end. Decide the population date and use the same date for every export so the campaign is internally consistent.
Days three and four: pull every export. Do it from each system console directly, not from the identity provider, and keep the raw files. Record row counts. Enrich each export with role or group, last activity, and grant date wherever the platform provides them. This is the step that determines whether the review is real or theatre.
Days five to nine: run the reviews. Send each reviewer only their own system, with the four decision states explained and a deadline. Chase daily. The realistic completion rate on a first campaign without chasing is poor, and chasing is the job. Reconcile the roster against the accounts while you wait, because the mismatches you find there are the ones that need explaining anyway.
Days ten to twelve: action the revocations and reductions, and collect the completion artifact for each one as it happens rather than at the end. Collecting them at the end is how they end up being screenshots taken a week later with no timestamp.
Days thirteen and fourteen: close. Zero undecided rows, zero outstanding revocations, exceptions written up with owners and dates, the whole package filed as one dated record against the control in each framework you are running. Then put the next campaign in the calendar immediately, because the gap between campaign one and campaign two is where most programs lose their cadence.
An access review run in isolation is a chore. Run as part of a program it is one of the highest-yield controls you have, because its outputs feed several other things auditors ask for.
The system list reconciles to the asset inventory, and a mismatch means one register is wrong. The leaver reconciliation feeds the offboarding control and the HR evidence. Exceptions feed the corrective action register, which is what a certification body looks at to decide whether your management system actually improves. Privileged account counts feed the risk assessment, and a rising count is a risk indicator you can put in front of management. Service account findings feed secrets rotation. Enterprise security questionnaires ask about review frequency and privileged access separately, and a real campaign record answers both without drafting.
The evidence itself should be filed once and mapped to every framework in play, rather than collected separately for each. One dated campaign record satisfies the logical access requirements of SOC 2 and the corresponding ISO 27001 Annex A expectations at the same time. Collecting once and mapping to many is the whole economics of running two frameworks, and it is the argument in our SOC 2 versus ISO 27001 guide. Keeping that mapped evidence current between audits is covered in keeping evidence fresh.
If you want to see what an auditor would ask for against your current position before you pay one, the evidence tracker and the auditor evidence simulator are free and will tell you which of the three assertions your current records fail.
| Population | Typical cadence | What the export and the record must contain |
|---|---|---|
| Identity provider users | Quarterly | All accounts including disabled and invited, with group memberships, MFA state, last sign-in, and creation date. Reconciled to the HR roster as at the same date. |
| Cloud console (production) | Quarterly, privileged monthly | Users, roles and attached policies, access keys with age, and anyone able to modify permissions. Root account usage and MFA state recorded separately. |
| Production database access | Quarterly | Database users, grants at the object level, any shared or application accounts, and whether access is direct or brokered through a bastion or a proxy with its own log. |
| Source repositories | Quarterly | Members and their permission level per repository or organization, outside collaborators, branch protection bypass rights, and deploy keys. |
| CI/CD and secrets store | Quarterly | Humans with pipeline edit rights, service connections, and every stored secret with its owner and last rotation date. Pipeline edit rights are production access. |
| SaaS behind single sign-on | Quarterly or semiannual | Exported from each application console, not the identity provider, so local accounts that bypass single sign-on appear. Admin seats listed explicitly. |
| Support and ticketing tools | Quarterly | Agents, their role, and whether the role can view customer records or impersonate a customer. Impersonation rights reviewed as privileged. |
| Service accounts and API tokens | Quarterly | Named human owner, purpose, scope, creation date, last used, and rotation or expiry. Any row with no owner is the first thing to fix. |
| Third-party OAuth grants | Quarterly or semiannual | Applications authorised into the workspace tenant, the scopes each holds, who authorised it, and a decision to keep or revoke on each. |
| Break-glass accounts | Quarterly plus after every use | Where the credential is held, who can retrieve it, the alert that fires on use, the last use with its reason, and the date the retrieval path was last tested. |
| Physical and building access | Semiannual, or per site | Badge holders per site with leaver badges deactivated, plus the visitor log for the period. Only applies where you hold in-scope sites. |
SOC 2 does not state a frequency. The Trust Services Criteria expect that access is reviewed periodically, and your own access control policy is what sets the interval. The auditor then tests you against your own commitment. Quarterly is the most common commitment and the one most enterprise buyers expect to see. Semiannual is defensible for a small, stable system if the policy says so and the risk assessment supports it. The expensive mistake is committing to quarterly in a policy and running two campaigns, because that is a control failure you reported on yourself.
Yes, and plenty of companies pass audits doing exactly that. The spreadsheet is not the problem. The problem is that spreadsheets rarely capture the completion half of the control, so the review records a decision to revoke eleven accounts and nothing proves the revocations happened. If you use a spreadsheet, add columns for the action date and the artifact reference, keep the raw export as a separate untouched file, and do not mark the campaign complete while any revocation is outstanding.
An access review is a contemporaneous record, which means it only exists if it was made at the time. A review dated last quarter cannot be produced today, and running one now does not cover the period that was missed. That period is permanently unevidenced. The practical options are to shorten the observation window so it excludes the missed period, to move the report date out, or to accept the exception in the report and show the corrective action. Which one is right depends on your customer commitments, and it is worth a conversation with the audit firm early rather than late.
The person who owns the system or manages the people on it, because they are the only ones positioned to know whether a given entitlement is still appropriate. The compliance owner runs the campaign, chases reviewers, files the record, and reports exceptions, but should not be the deciding reviewer. An auditor who sees a single name against every decision in a 180-row population will ask on what basis that person made 180 judgements, and the honest answer is usually that they did not.
Yes, and they are the population most often left out. A service account is an identity with entitlements, usually broad ones, usually created quickly to solve a problem and rarely revisited. Each one needs a named human owner, a stated purpose, a recorded scope, and a rotation or expiry date, and each needs a keep, reduce or revoke decision the same as a person does. The question is different, though. You are not asking whether someone still works here, you are asking whether the integration still exists, whether the scope is still justified, and when the credential was last rotated.
An artifact produced by the system, carrying a date. That can be an audit log entry showing the deactivation with a timestamp and the actor, a fresh export of the same population showing the account absent or disabled, or a ticket that contains both the request and the system confirmation. A tick in a tracker column with nothing behind it is not proof, because it is the compliance owner asserting that somebody else did the work, and that assertion is the exact thing the auditor is testing.
It depends on how often the control operates, the assessed risk, and the firm. For a quarterly control over a twelve-month window there are four occurrences and auditors commonly test two. For a monthly control they commonly test two to four of the twelve. Within a selected campaign they will then trace individual rows, often somewhere between five and twenty-five depending on population size. A control that had an exception in the prior year attracts a larger sample. What matters on your side is that your own population sizes and selections are recorded and re-derivable, so the sample can be reconstructed rather than argued about.
Tooling helps with the parts that are mechanical. It can pull user lists from connected systems, route the review to a named person, and hold the decision record with dates, which removes most of the administrative burden and makes the evidence consistent. It does not define your population, decide which systems are in scope, judge whether an entitlement is appropriate, or perform the revocation. It also cannot see accounts in systems it is not connected to, and those are exactly the systems where the stale local accounts live. The tool changes who does the typing, not who does the thinking.
traztech Workspace has every control of whichever frameworks apply to you, written in plain English, with somewhere to attach the proof. Free to use, with no card and no trial clock.
No credit card, no trial clock, no locked features. We make money when someone wants help closing the gaps, not from the Workspace.
| traztech Workspace | Other GRC platforms | |
|---|---|---|
| Licence cost | $0. Free forever, no card, no paid tier | $7,500 to $50,000 a year, on an annual contract |
| Control library, evidence register, policy templates, risk register, vendor questionnaires, readiness scoring | Included | Included |
| What it costs inside an engagement with us | $0. You need a workspace either way | Unchanged. The subscription sits on top of the fee |
| What it does to your audit quote | $11,000 off a five-figure quote on one engagement, for a documented readiness position | Nothing. The audit firm prices your readiness, not your tooling |
Pricing in the right column is what compliance automation platforms are publicly reported to charge; none of them publish a number, so treat it as a range rather than a quote. The $11,000 came off the audit firm's own number once the readiness position was documented (the engagement). Where a paid platform is the better buy, and the fuller comparison, is on the Workspace page.
We build the populations, assign the reviewers, chase the decisions, and collect the proof that the revocations happened, so the record survives sampling instead of creating findings.
Book a strategy callWant the human version?
Jacob sends a few short, practical notes on getting security and compliance right without the months of pain. No fluff, unsubscribe in one click. Reply anytime; it reaches him directly.
From Jacob Masse, founder of traztech. No spam, unsubscribe in one click.
Track record
Five published CVEs. CVE-2024-45163 (CVSS 9.1) is a flaw in the Mirai botnet itself, which gave defenders a way to shut down attacker infrastructure. CVE-2026-42626 takes HP ENVY 5000 printers offline from any unauthenticated device on the same network.
At Humera, a venture-backed US security company, Jacob built the compliance programme in-house from nothing: no report, no policies, no documented controls. It ended in a Type II attestation across 76 controls with zero exceptions, on a team of 15.