Offboarding Collaborator Cross-Check
Daily flow compares List Repository Collaborators output against the Azure AD Active Employees group; anyone no longer active triggers a ServiceNow-style ticket via HTTP for manual removal review.
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
Every weekday morning at 06:00 Eastern, this flow cross-references the collaborators on a configurable list of GitHub repositories against the Active Employees group in Microsoft Entra ID. Anyone present in GitHub but missing from the group is treated as potentially offboarded — the flow opens a ServiceNow incident for manual removal review and rolls every hit into a single HTML summary email sent to the Security Operations distribution list. On clean days the flow stays silent.
Use Case
External GitHub collaborators routinely outlive the engineers who added them — contractors finish projects, employees leave, and nobody remembers to revoke repo access. This flow gives Security Operations a daily, automated, zero-effort answer to the question "who has access to our repos that shouldn't?" without forcing IT to maintain a parallel access spreadsheet.
The flow is ideal for teams that:
- Daily detection of stale GitHub collaborators against the canonical employee group.
- Auto-create ServiceNow incidents so removal flows through the existing change process.
- Single consolidated email per day — no per-collaborator spam.
- Word-boundary CSV match avoids alex matching alexander.
- Multi-repo from one env var — add a repo by editing a string, no flow change.
Flow Architecture
Recurrence — Daily 06:00 EST
TriggerDaily run, time zone Eastern.
Initialize 8 variables
InitializeVariablevarRepoList, varActiveGroupId, varSnowEndpoint, varSecEmail, varOrg, varHitsHtml, varTicketsCreated, varActiveLoginsCsv — bound from the five env vars + accumulators.
Get_Active_Employees_Group_Members
Azure AD GetGroupMembersPulls every member of the Active Employees group with top=999.
Project_Active_Logins
SelectMaps each member to lowercase mailNickname, falling back to UPN local-part; coalesce keeps it null-safe.
Set_varActiveLoginsCsv
SetVariableJoins the projection as `,login1,login2,...,` — leading/trailing commas turn `contains()` into a word-boundary check.
Apply_To_Each_Monitored_Repo
ForeachIterates split(varRepoList, ','). Inner: List_Repository_Collaborators → Apply_To_Each_Collaborator → Check_If_Collaborator_Inactive (If). Inactive branch: HTTP POST to ServiceNow, append a row to the HTML report, increment counter.
Check_If_Any_Inactive_Found
If conditionBranches on greater(varTicketsCreated, 0) to avoid quiet-day spam.
- Send_Offboarded_Summary_Email — Office 365 SendEmailV2 with HTML body, High importance, sent to the Security Operations distribution list.
Empty branch — no email is sent on clean days.
Environment Variables
| Schema name | Type | Default | Description |
|---|---|---|---|
| flowlibs_MonitoredRepoList | String | — | Comma-separated `owner/repo` pairs to audit (e.g. `yourcompany/internal-ops,yourcompany/pipeline-utils`). |
| flowlibs_ActiveEmployeesGroupId | String | — | Entra group object ID of the canonical Active Employees group. |
| flowlibs_ServiceNowEndpoint | String | — | ServiceNow Table API URL or Scripted REST endpoint for incident.do. |
| flowlibs_SecurityTeamEmail | String | — | Distribution list that receives the summary email. |
| flowlibs_GitHubOrganization | String | — | GitHub org slug — used in logging and email footer. |
Connectors & Connections
| Connector | API name | Actions used |
|---|---|---|
| Azure AD | shared_azuread | GetGroupMembers (pulls every member of the Active Employees group) |
| GitHub | shared_github | ListCollaborators |
| Office 365 Outlook | shared_office365 | SendEmailV2 (delivers the daily HTML summary) |
| HTTP | shared_http | POST (ServiceNow incident.do for each stale collaborator) |
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.
- Different ITSM
- Replace the HTTP POST with a Jira CreateIssue or Zendesk CreateTicket action and drop the flowlibs_ServiceNowEndpoint env var.
- Tighten cadence
- Change Recurrence to hourly for higher-risk repos — be mindful of GitHub API rate limits.
- Multi-group active source
- Replace flowlibs_ActiveEmployeesGroupId with a JSON array env var and union the members across all groups before building the CSV.
- Auto-remove instead of ticket
- Swap the HTTP action for GitHub RemoveCollaborator for a fully automated revocation — only after burn-in.
- Skip bots
- Add a Filter Array after List_Repository_Collaborators excluding logins ending in [bot] or matching a bot allowlist.
- Per-repo owner email
- Replace the single flowlibs_SecurityTeamEmail with a JSON map of repo → owner email and look up dynamically inside the Foreach.
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.01Read repo list env var
Hydrates the CSV repo list at flow start.
EXPR.02Build active-logins CSV
Wraps the joined logins in commas so contains() is word-boundary-safe.
EXPR.03Project member to lowercase login
Falls back to UPN local-part if mailNickname is missing.
EXPR.04Word-boundary inactive check
Drives the per-collaborator If condition.
EXPR.05Decide whether to email
Skips the daily mail entirely on clean days.
Comments
Sign in to join the conversation.
Sign inNo comments yet. Be the first to share your experience with this flow.