Group Membership Audit Reporter
Weekly scheduled flow pulls Azure AD group memberships, compares against an approved list in SharePoint, and flags discrepancies.
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
FlowLibs - Group Membership Audit Reporter runs every Monday at 8:00 AM Eastern, reads a SharePoint list of approved Entra ID security-group memberships, fetches the current member list of each group from Microsoft Entra ID, compares the two sets, and emails the Security Team a single HTML report listing every drift — both unauthorized members (in the group but not on the approved list) and missing members (on the approved list but not in the group).
The flow is the canonical "weekly compliance scan with single-email digest" pattern for FlowLibs: scheduled trigger, SharePoint as the system-of-record for expectations, Entra ID as the system-of-record for actuals, comma-wrapped CSV for word-boundary-safe set comparison, two pre-filtered iterators that build HTML rows for each discrepancy class, and one consolidated email at the end.
Use Case
Who: IT Admins, Security & Compliance Operations. When: weekly governance audit of privileged group memberships. Why: groups drift over time as people join, leave, change roles, or get added ad hoc. Without a periodic comparison against an authoritative "should be" list, drift goes unnoticed until the audit. This flow turns the audit from "manually export memberships and diff them in Excel" into "open the Monday morning email and triage."
The flow is intentionally *report-only* — it does not auto-remove unauthorized members or auto-add missing ones. Surface, don't enforce. Remediation stays in human hands so that a misconfigured approved list cannot trigger a self-inflicted access incident.
The flow is ideal for teams that:
- IT Admins running weekly governance audits of privileged group memberships
- Security & Compliance Operations teams who need to detect drift between approved and actual membership
- Organizations that prefer surface-don't-enforce reporting so a misconfigured approved list cannot trigger a self-inflicted access incident
- Teams replacing manual Excel-diff audits with a single Monday-morning triage email
Flow Architecture
Weekly Group Membership Audit
RecurrenceFrequency Week, interval 1, Monday 08:00 Eastern, startTime 2026-04-27.
Initialize Variables (7 chained)
Initialize VariableHydrate env-var params plus counters and accumulators: varSharePointSiteUrl, varApprovedListName, varSecurityTeamEmail, varDiscrepancyRowsHtml, varTotalDiscrepancies, varGroupsAudited, varActualMembersCsv.
Get Approved Memberships From SharePoint
SharePoint - Get itemsdataset ← site URL, table ← list display name, $top: 500. Pulls every row of the approved-memberships list.
Apply To Each Approved Group
Foreach (concurrency 1)For each approved-memberships row, fetch live Entra ID members, project UPNs, build comma-wrapped CSVs, run two Filter Array queries (unauthorized + missing), then iterate each filtered set to append HTML rows to varDiscrepancyRowsHtml and increment varTotalDiscrepancies. Inner steps: Get_Group_Members_From_Entra ($top: 999), Project_Actual_Member_UPNs (Select — lowercased UPN with mail fallback), Set_varActualMembersCsv (comma-wraps the actual UPN list), Compose_Approved_Members_Csv (comma-wraps the row's ApprovedMembers text, lowercased, whitespace-stripped), Compose_Approved_Members_Array (splits the same string into an array), Filter_Unauthorized_Members (actuals not in approved CSV), Filter_Missing_Members (approved not in actual CSV), For_Each_Unauthorized_Member (appends styled <tr> + increments counter), For_Each_Missing_Member (same pattern, different label color), Increment_Groups_Audited (+1 to varGroupsAudited).
Compose Email Body
ComposeBuilds the full HTML digest with run metadata, summary table, and discrepancy table. Uses an if() to substitute an "All audited groups match" row when varTotalDiscrepancies is zero so the email is always positive confirmation that the scan ran.
Send Audit Report Email
Office 365 Outlook - Send an email (V2)Environment Variables
| Schema name | Type | Default | Description |
|---|---|---|---|
| flowlibs_SharePointSiteURL | String | https://your-tenant.sharepoint.com | Root URL of the FlowLibs SharePoint site hosting the approved-memberships list. |
| flowlibs_ApprovedMembershipsListName | String | FlowLibs - Approved Group Memberships | Display name of the SharePoint list that stores the approved Entra ID group memberships. |
| flowlibs_SecurityTeamEmail | String | securityteam@your-tenant.onmicrosoft.com | Recipient (distribution list or shared mailbox) of the weekly audit digest email. |
Connectors & Connections
| Connector | API name | Actions used |
|---|---|---|
| SharePoint | shared_sharepointonline | GetItems (Get_Approved_Memberships_From_SharePoint) |
| Microsoft Entra ID | shared_azuread | GetGroupMembers (Get_Group_Members_From_Entra — requires Group.Read.All and User.Read.All (or GroupMember.Read.All)) |
| Office 365 Outlook | shared_office365 | SendEmailV2 (Send_Audit_Report_Email) |
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.
- Change the cadence
- Edit the Recurrence trigger — adjust frequency (Day / Week / Month), interval, schedule.weekDays / hours / minutes. Keep within Eastern Standard Time or swap timeZone for another Windows time-zone identifier.
- Add or remove groups
- Add or delete rows in the SharePoint approved-memberships list. No flow edits required — the flow re-reads the list on every run.
- Tighten or loosen the approved-list parsing
- Today's flow lowercases the ApprovedMembers cell and strips spaces before splitting on commas. To change the delimiter (semicolon, newline) or preserve casing for case-sensitive directories, edit Compose_Approved_Members_Csv and Compose_Approved_Members_Array. The CSV-wrap (leading + trailing comma) and word-boundary contains() check stay the same.
- Switch from email to Teams or Adaptive Card
- Replace Send_Audit_Report_Email with shared_teams/PostMessageToConversation (slash-path body/recipient/groupId, body/recipient/channelId, body/messageBody) and re-template the HTML to fit the Teams preview. Or add an Approvals action in front of the email so the digest needs an admin click before going out.
- Persist the audit results
- Append a Dataverse CreateRecord action after the email pointing at a new flowlibs_groupauditrun table with columns for run date, groups audited, total discrepancies, and a JSON snapshot of the discrepancy rows — gives the security team a queryable history of past scans.
- Remediate inline (advanced — not recommended for a first deploy)
- Inside the unauthorized-members Foreach, add an Entra ID RemoveMemberFromGroup action gated by an Approvals card. Inside the missing-members Foreach, add AddMemberToGroup. Switching from report-only to enforcement is a meaningful risk shift — pair with a "dry run" env var that defaults to true so the first weeks ship as report-only and only flip to enforcement after the team is comfortable with the discrepancy volume.
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.01Word-boundary set-membership CSV build
Produces ,user1,user2,user3, — the leading and trailing commas turn substring contains() into an exact-token match (no false positives like alex matching alexander).
EXPR.02Word-boundary set-membership lookup
Pair with the CSV-build above to safely test whether target exists in the comma-wrapped list.
EXPR.03Empty-array safety on Get items / Get group members
Turns a missing 'value' key into iterate-zero-times instead of a runtime null-iteration failure. The same pattern wraps Get_Group_Members_From_Entra.
EXPR.04Lowercase actual UPNs with mail fallback
Case-tolerant projection used inside Project_Actual_Member_UPNs. Falls back to mail for members without a UPN and to empty string for guests with neither.
EXPR.05Lowercase + whitespace-strip the approved-members cell
Used in Compose_Approved_Members_Csv to make the comparison case- and whitespace-tolerant before CSV-wrapping.
EXPR.06Subject-line auto-summary
Recipients can triage from the inbox preview without opening the email.
EXPR.07All-clear empty-state for the email body
The email is always-on; a zero-discrepancy run is positive confirmation that the scan ran, not silence.
Comments
Sign in to join the conversation.
Sign inNo comments yet. Be the first to share your experience with this flow.