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.
Overview
Every weekday morning at 06:00 ET, cross-references 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 — opens a ServiceNow incident for manual removal review and rolls every hit into a single HTML summary email sent to Security Operations. On clean days the flow stays silent.
**State:** Stopped
Use Case
External GitHub collaborators routinely outlive the engineers who added them — contractors finish projects, employees leave, nobody remembers to revoke repo access. This flow gives Security Operations a daily, automated, zero-effort answer to "who has access to our repos that shouldn't?" without forcing IT to maintain a parallel access spreadsheet.
**Key Benefits:** daily detection; auto-creates ServiceNow incidents; single consolidated email per day; word-boundary CSV match avoids false positives; defensive coalesce around connector bodies.
Flow Architecture
Trigger: **Recurrence — Daily 06:00 EST**. 1. Init 8 vars (repo list, active group ID, SNow endpoint, sec email, org, hits HTML, tickets created, active logins CSV) 2. **Get_Active_Employees_Group_Members** (Azure AD GetGroupMembers $top=999) 3. Project_Active_Logins (Select to lowercase mailNickname or UPN local-part) 4. Set_varActiveLoginsCsv to `,login1,login2,...,` (word-boundary) 5. **Apply_To_Each_Monitored_Repo**: - List_Repository_Collaborators (GitHub ListCollaborators per_page=100) - Apply_To_Each_Collaborator: Check_If_Collaborator_Inactive (If); if inactive: Create_ServiceNow_Incident (HTTP POST), Append_Offboarded_Hit_Row, Increment_Tickets_Created 6. **Check_If_Any_Inactive_Found** (If) → Send_Offboarded_Summary_Email (Outlook High importance)
Environment Variables
| Variable | Schema Name | Type | Default | Purpose |
|---|---|---|---|---|
| Monitored Repo List | flowlibs_MonitoredRepoList | String | (none) | Comma-separated owner/repo to audit |
| Active Employees Group ID | flowlibs_ActiveEmployeesGroupId | String | (none) | Entra group object ID of the Active Employees group |
| ServiceNow Endpoint | flowlibs_ServiceNowEndpoint | String | (none) | ServiceNow Table API URL or Scripted REST endpoint for incident.do |
| Security Team Email | flowlibs_SecurityTeamEmail | String | (none) | Distribution list that receives the summary |
| GitHub Organization | flowlibs_GitHubOrganization | String | (none) | GitHub org slug |
Connectors & Connections
| Connector | API Name | Connection Format | Usage |
|---|---|---|---|
| Azure AD | shared_azuread | Service account with GroupMember.Read.All | GetGroupMembers — pulls Active Employees group |
| GitHub | shared_github | PAT or OAuth (repo scope) | ListCollaborators per monitored repo |
| Office 365 Outlook | shared_office365 | SecOps sender mailbox | SendEmailV2 — daily summary |
| HTTP | shared_http | Endpoint via env var | POST to ServiceNow incident.do |
Customization Guide
1. Import; set 5 env vars; bind 3 connection refs; run Flow Checker; manual test; turn on.
**Common Modifications:** different ITSM (Jira CreateIssue, Zendesk); tighten cadence to hourly; multi-group active source via JSON array env var; auto-remove via GitHub RemoveCollaborator (after burn-in); skip bots via Filter Array; per-repo owner email via JSON map.
Key Expressions
- @parameters('flowlibs_MonitoredRepoList') — read repo list
- @concat(',', join(body('Project_Active_Logins'), ','), ',') — build active logins CSV
- @toLower(coalesce(item()?['mailNickname'], split(item()?['userPrincipalName'], '@')[0])) — lowercase login projection
- @not(contains(variables('varActiveLoginsCsv'), concat(',', toLower(item()?['login']), ','))) — word-boundary inactive check