Cross-Group Membership Report
Generate a matrix of users and their Office 365 Group memberships, export to Excel for license and access planning.
Provided as-is, without warranty of any kind. Review and test each pattern in a non-production environment before deploying it to live automations. See our Terms.
Overview
A scheduled cloud flow that produces a weekly user x Office 365 Group membership matrix as a CSV file in SharePoint. Each row of the export represents one user-to-group relationship, so a single user appearing in five groups produces five rows. Open the CSV in Excel and pivot, filter, or join against your license inventory to drive license reclamation, access reviews, and cleanup of stale group memberships.
The flow runs automatically every Monday at 06:00 UTC, enumerates all Microsoft 365 groups in the tenant, walks each group's membership, writes a single timestamped CSV file to a configurable SharePoint folder, and emails a summary of the run (groups enumerated, total memberships, file name) to the configured recipient.
Use Case
This flow turns Microsoft 365 group membership into a flat, analyzable CSV so admins can run access reviews, license optimization passes, joiner/mover/leaver audits, HR org-chart reconciliation, and compliance evidence collection without manually exporting from the admin center each time.
The flow is ideal for teams that:
- Quarterly access reviews — pivot the CSV by Group Name to validate every member belongs in every group
- License optimization — join Group ID against your Azure AD licensing groups to find users in licensed groups who haven't logged in
- Joiner / Mover / Leaver audits — diff this week's CSV against last week's to catch unexpected adds or drops
- HR or department reorg validation — filter by group naming convention to confirm department membership matches the latest org chart
- Compliance evidence — drop the weekly CSVs into a retention-protected library for a point-in-time record of group access
Flow Architecture
Recurrence
RecurrenceRuns weekly, Monday at 06:00 UTC.
Init varSiteUrl
InitializeVariableReads the `flowlibs_SharePointSiteURL` environment variable into a string.
Init varReportFolder
InitializeVariableReads the `flowlibs_GroupMembershipReportFolder` environment variable.
Init varEmailRecipient
InitializeVariableReads the `flowlibs_NotificationEmailAddress` environment variable.
Init varMaxGroups
InitializeVariableReads the `flowlibs_GroupMembershipMaxGroups` environment variable and casts it to an integer via `int(...)`.
Init varCsvHeader
InitializeVariableInitializes the CSV header row: `"User Display Name","User Email","User Principal Name","Group Name","Group ID"\n`.
Init varCsvRows
InitializeVariableEmpty string accumulator that will collect the per-member CSV rows.
Init varGroupCount
InitializeVariableInteger counter, starts at 0; tracks how many groups were enumerated.
Init varMemberCount
InitializeVariableInteger counter, starts at 0; tracks total user-to-group memberships written.
List All Groups
OpenApiConnection (Office 365 Groups — ListGroups)Lists every Microsoft 365 group in the tenant. Uses `$top: @variables('varMaxGroups')` as a safety cap.
Environment Variables
| Schema name | Type | Default | Description |
|---|---|---|---|
| flowlibs_SharePointSiteURL | String | https://your-tenant.sharepoint.com | Root URL of the SharePoint site that holds the report library. |
| flowlibs_NotificationEmailAddress | String | you@yourcompany.com | Mailbox or distribution list that receives the run-summary email. |
| flowlibs_GroupMembershipReportFolder | String | /Shared Documents/FlowLibs - Group Membership Reports | Server-relative folder path where the timestamped CSV is written. |
| flowlibs_GroupMembershipMaxGroups | String | 200 | Cap on how many Microsoft 365 groups to enumerate per run; cast to integer at runtime. Raise for large tenants, lower for testing. |
Connectors & Connections
| Connector | API name | Actions used |
|---|---|---|
| Office 365 Groups | shared_office365groups | ListGroups ListGroupMembers |
| SharePoint | shared_sharepointonline | CreateFile (writes the CSV report) |
| Office 365 Outlook | shared_office365 | SendEmailV2 (sends the run summary) |
Note — All connections are referenced as solution connection references; the flow is portable between environments as long as a connection is mapped at import time.
Customization Guide
Almost every realistic variant of this flow can be implemented by changing environment variable values. A few cases require small edits inside the flow definition — those are called out explicitly below.
- Deploy to your tenant
- Import the solution package, then point each environment variable at your own values: SharePoint site root, server-relative report folder, notification mailbox, and the max-groups cap. Authorize the three connection references with a service account that holds Group.Read.All (Microsoft Graph) plus write permission to the report folder, then turn the flow On.
- Run more often
- In the Recurrence trigger, change frequency to Day or Hour and remove the weekDays parameter.
- Filter to security/mail-enabled groups only
- Add $filter: securityEnabled eq true and mailEnabled eq true as a parameter on the List All Groups action.
- Filter to a single group prefix
- Add $filter: startswith(displayName, 'dept-') to List All Groups — useful for narrow audits scoped to one naming convention.
- Add columns to the CSV
- Append fields to Init varCsvHeader and to the Append Member Row To Csv expression. The Graph members payload exposes jobTitle, department, accountEnabled, and more.
- Write to Excel instead of CSV
- Replace Save Csv To SharePoint with Excel Online AddRowToTable and remove the Init varCsvHeader / Init varCsvRows steps. Requires a pre-existing .xlsx with a defined Table.
- Email the CSV as an attachment
- Add an emailMessage/Attachments
Key Expressions
The flow is intentionally light on Power Fx / WDL gymnastics — the heaviest expressions are the branch-name concatenation and the approval outcome check. They are listed below in the order they appear in the flow.
EXPR.01Cast env var to integer for $top
Used in Init varMaxGroups. Environment variables are always String type, so cast to integer before using as `$top` on List All Groups.
EXPR.02Sortable timestamped report filename
Used in Compose Report Filename. Lexicographic sort matches chronological order, so file listings are naturally time-ordered.
EXPR.03RFC 4180 CSV escaping for a member field
Used inside Append Member Row To Csv. Doubles internal quotes per RFC 4180; `coalesce` turns null fields into empty strings so each row keeps the right number of columns.
EXPR.04Final CSV body for SharePoint upload
Used as the body of Save Csv To SharePoint. Header is prepended at write time so partial-failure runs still leave a parseable file.
EXPR.05Cap List All Groups with $top
Used as `$top` on List All Groups. Hard cap to prevent runaway enumeration in tenants with thousands of groups; pagination via `$skiptoken` can be added if needed.
EXPR.06Pass group id to inner ListGroupMembers call
Used as the `groupId` parameter on List Group Members. The current group's GUID is taken from the outer Foreach item.
Comments
Sign in to join the conversation.
Sign inNo comments yet. Be the first to share your experience with this flow.