A Microsoft 365 group access review is how you decide who still belongs to the group that backs a Team, a SharePoint site, and a Planner board. The group is one object with two lists that matter: members and owners. Owners can add members and guests after the campaign closes. A review that only snapshots members, and never asks about owners, expires the moment an owner invites the next contractor.
The compliance lead usually discovers this when the auditor samples a Team that holds customer files. The member export looks reviewed. The owner is a departed employee, and three guests arrived the week after the campaign. Rutagon builds the extract from Microsoft Graph, separates owner decisions from member decisions, and files both in the GRC you already run.
Members, owners, and guests are different decisions
A Microsoft 365 group has groupTypes containing Unified. Teams membership is that group. We list groups with that type and read /members and /owners separately. Owners are often also members. That overlap is not a reason to skip the owner list. The owner right is the right to change the member list.
Guests are members whose userType is Guest. They need their own decision because the business reason is different: a partner on a channel is not an employee in a department distribution list. We mark guest rows so the reviewer cannot approve them with the same gesture they use for coworkers.
Dynamic groups are a third case. When groupTypes also contains DynamicMembership, the member list is the output of membershipRule. Approving today's members does not constrain tomorrow's rule evaluation. The review item is the rule text and the owners, plus a sample of the members the rule produced. A reviewer who cannot see the rule is approving a moving set. Nested group access review picks up the next problem: this group nested inside another group, where the rule's output becomes someone else's entitlement.
GET /groups?$filter=groupTypes/any(c:c eq 'Unified')&$select=id,displayName,mail,membershipRule,groupTypes
GET /groups/{id}/owners?$select=id,userPrincipalName,accountEnabled
GET /groups/{id}/members?$select=id,userPrincipalName,userType,accountEnabled
Microsoft documents the member list operation in the List group members reference. We follow that API. We also call the owners navigation, which the member list does not replace.
Microsoft 365 group access review of owners and members
The campaign row is a principal on a group in a role: member or owner. The reviewer is a living owner. If every owner is disabled, the group is an owner-repair exception and does not auto-approve. Disabled members are revoke candidates even when nobody opens the campaign, and we still record the revoke.
Approve means this person still needs that role on that group. Revoke means we call Graph to remove the member or owner ref. Removing the last owner is blocked until a replacement owner is written. A group with zero owners becomes an unowned SharePoint site with files still shared. We refuse that end state.
After the API remove, we re-read the navigation. The evidence row stores the group id, the role, the principal, the decision, the actor, and the second read. That packet is filed in the GRC you already run. Entra's built-in access review can host the human step when it fits the tenant. We still keep the extract and the proof read, because a completed Entra campaign that did not include owners or guests is not the population you think it is.
Private channels in Teams add another group. A reviewer who only attests the parent Team misses the private channel membership. We include groups that Teams created for private and shared channels when those channels hold in-scope files. The filter is "this group grants access to in-scope data," not "this group looks like a department name."
What the first campaign always finds
The first extract shows owners who are external guests, service accounts that are members because a flow posted as them, and mail-enabled security groups nested as members. Nested principals are expanded far enough to name a user, or they are handed to the nested-group campaign with the path attached. A row that says "group X is a member" without a path is not a decision a human can make.
Orphaned groups from old projects show up with files still in SharePoint. Access review does not delete the site. It forces an owner to exist or it forces the group to be retired through the lifecycle process you already have. We do not hide unused groups to shrink the campaign. Unused plus ownerless is the finding.
Role change is the steady-state value. A person who moves departments keeps every Team they joined unless this review or a mover job removes them. Pair the campaign with access review after a role change so the trigger is not only the annual calendar. The annual campaign remains, because SOC 2 samples a periodic recertification. The mover path keeps the annual list smaller and more believable.
Application permissions and site sharing links are adjacent and easy to confuse with group membership. A sharing link to a file is not a group member. We do not claim this campaign closes anonymous links. If links are in your audit scope, they are a separate extract. Mixing them into the group decision teaches reviewers to ignore both.
Evidence the auditor can sample
For a sampled group we can show the member list at review time, the owner list, the guest flag, the dynamic rule if one exists, the human decision, and the directory read after revoke. The GRC ticket id ties the packet to the control you already test. Rutagon does not certify your tenant. We build the pipeline that makes the test possible.
Large tenants do not get one campaign of every group on earth. We scope to groups that grant access to in-scope systems: the Teams and sites named in the system description, plus any group those owners identify as holding the same data. A scope memo is part of the evidence. An unbounded "all groups" dump looks thorough and does not get reviewed.
Delta runs come next. New owners, new guests, and rule edits since the last decision are the weekly list. The full population is still exportable for the sample. Reviewer time goes to changes and to groups that failed the living-owner test.
Graph throttling is a real constraint on tenants with tens of thousands of groups. We page, we back off, and we checkpoint the group id so a failed run resumes. A partial extract that looks complete is worse than a slow one. The job records the last group id it finished.
Frequently Asked Questions
Why review owners separately from members?
Owners can add members and guests after the campaign ends. A member snapshot goes stale as soon as an owner invites someone. The owner decision is its own row, and we block removal of the last owner until a replacement exists.
How are dynamic Microsoft 365 groups reviewed?
The membership rule is the entitlement. We present the rule text, the owners, and a sample of the members the rule produced. Approving only the current member list leaves the rule free to add people the next time it evaluates.
Do private Teams channels show up?
Private and shared channels have their own groups. When those channels hold in-scope files, their groups are in the population. Reviewing only the parent Team leaves the private channel membership unattested.
What happens to guest users?
Guests are member rows flagged by user type. The reviewer makes an explicit decision for each guest. They are not folded into the employee approve-all action, because the business reason and the offboarding path are different.
Where are the decisions stored?
Approve and revoke decisions, including the second directory read, are filed in the GRC you already run. Entra can host the review workflow when it covers the population. The evidence packet still lands in your GRC for the auditor.
See what an Access & Credential Governance Diagnostic finds in your environment. Contact Rutagon at rutagon.com/contact, 907-841-8407, or contact@rutagon.com.