2025-08-05
Map Microsoft 365 Users, Groups, Files, and Shares with Graph

Follow the Threads: Using Graph Explorer to Reveal Who’s Really Touching Your Sensitive Files in M365
What this episode covers
-
Why SharePoint, Teams, and OneDrive aren’t silos (and how access ripples through groups)
-
How to trace a single user’s footprint across memberships, files, links, and activity
-
Precision filtering in Graph Explorer to cut noise and surface real risk
-
Pagination and scale tactics for large tenants
-
A practical framework to turn raw Graph calls into living visibility and governance
Key takeaways
-
Access is relational, not location-based: group changes cascade to files across services.
-
Admin UIs show fragments; Graph lets you pivot end-to-end (user → groups → sites → files → links → viewers).
-
Filters ($filter, $select, $top) turn firehose outputs into actionable lists.
-
Pagination (nextLink) is mandatory at scale—page 1 is rarely the full story.
-
Make this repeatable: automate queries, normalize IDs, and feed results to Power BI for leaders.
Why M365 isn’t isolated
-
Teams chats, SharePoint libraries, and OneDrive folders cross-pollinate via group membership.
-
“One add to one group” can unlock many sites, channels, and external shares without obvious audit continuity.
-
Compliance risk = invisible pathways (guest links in chats, files mirrored across libraries, orphaned shares).
Footprint mapping with Graph Explorer (user to file touchpoints)
-
Anchor on the user
GET /v1.0/users/{userPrincipalName}?$select=id,displayName,userPrincipalName -
Enumerate memberships
GET /v1.0/users/{id}/memberOf?$select=id,displayName -
Pivot to resources those groups back
• For M365 Groups/Teams → underlying SharePoint site:
GET /v1.0/groups/{groupId}/drive/root/children?$select=id,name,lastModifiedDateTime,webUrl
• Or enumerate site drives and libraries:
GET /v1.0/sites/{siteId}/drives -
Pull item permissions and links
GET /v1.0/drives/{driveId}/items/{itemId}/permissions?$select=id,grantedToV2,link,roles,shareId -
Trace who accessed what, when (where available)
GET /v1.0/auditLogs/signIns or /security/alerts (pair with file, site, or sharing context) -
Close the loop to Teams posts
GET /v1.0/teams/{teamId}/channels/{channelId}/messages?$search="filename" (requires beta/permissions)
Noise-killing filters you’ll actually use
-
External shares in the last 7 days:
GET /v1.0/drives/{driveId}/items/{itemId}/permissions?$filter=link/scope eq 'anonymous' or link/scope eq 'organization'&$select=… -
Only risky file types:
Append $filter=endsWith(resourceReference/name,'.xlsm') or endsWith(resourceReference/name,'.pst') -
After-hours mentions of sensitive terms in Teams:
GET /v1.0/teams/{teamId}/channels/{channelId}/messages?$search="confidential"
Then post-filter by createdDateTime outside business hours. -
Keep payloads lean:
Always add $select to return just ids, names, webUrl, lastModifiedDateTime, and minimal permission fields. -
Cap for triage:
Use $top=50 for “latest first” spot checks; remove when exporting to a data store.
Enterprise-scale musts (pagination and throttling)
-
You are not done until you follow @odata.nextLink for every page.
-
Respect 429s: implement exponential backoff and resume where you left off.
-
Batch wisely: prefer fewer, larger requests over many tiny ones; cache group→site mappings.
-
Partition big crawls by site or date to avoid timeouts; schedule off-peak.
Common pitfalls (and fast fixes)
-
Pitfall: CSV exports drift as schemas change.
Fix: Use Graph API with versioned transforms and “unknown” field handling. -
Pitfall: Mixed identifiers (GUIDs vs UPNs) break joins.
Fix: Normalize to a semantic model (Users, Groups, Sites, Drives, Items, Permissions). -
Pitfall: “Page 1 bias” hides most incidents.
Fix: Always log total pages processed; alert if expected volume drops. -
Pitfall: Floods of irrelevant files.
Fix: Filter for link/scope, created/modified windows, and sensitive term lists. -
Pitfall: Teams shares not tied back to files.
Fix: Correlate channel message links (webUrl) to drive items; store both IDs.
Executive-level KPIs this unlocks
-
External exposure velocity: new guest links per week (by site/team)
-
Sensitive file touch rate: unique viewers on labeled content (internal vs external)
-
Group ripple risk: files newly accessible within 24h of membership changes
-
Shadow circulation: files appearing across multiple teams/sites within a sprint
-
Cleanup effectiveness: revoked link count and time-to-remediation
Quick-start query recipe (copy/paste flow)
-
User core → groups:
/users/{upn}?$select=id,displayName
/users/{id}/memberOf?$select=id,displayName -
For each M365 group:
/groups/{groupId}/drive/root/children?$select=id,name,webUrl,lastModifiedDateTime&$top=200
Follow @odata.nextLink -
For each item:
/drives/{driveId}/items/{itemId}/permissions?$select=id,roles,grantedToV2,link
Filter to link/scope in ('anonymous','organization') -
Export rows to your store (include tenantTime, siteId, driveId, itemId, webUrl, owner, scope, expires)
-
Visualize in Power BI (see below)
Turning this into a living dashboard
-
Dataflows: land normalized tables (Users, Groups, Sites, Drives, Items, Permissions, Messages).
-
Model: star schema with Items at the center; relationships to Users (owners/viewers) and Groups.
-
Refresh: incremental by lastModifiedDateTime; intra-day for hot sites.
-
Alerts: trigger on new external links to labeled content or group-ripple spikes.
30-day rollout plan
Week 1: App registration, least-privileged Graph perms, baseline queries with $select/$filter.
Week 2: Pagination scripts, normalization (IDs → UPNs/device names), first export.
Week 3: Power BI model + KPIs (external exposure, ripple risk, dwell to revoke).
Week 4: Automate refresh, add anomaly alerts, document SOP for investigations.
Who should watch
-
M365 admins and SecOps leads who suspect their “green” dashboards hide gray areas
-
Compliance teams needing end-to-end share visibility without third-party sprawl
-
Collaboration owners accountable for safe external sharing
Field tips
-
Always start from the user or the file—then pivot both directions to avoid blind spots.
-
Treat “add to group” as a high-risk change; review downstream access within 24 hours.
-
Keep a shortlist of sensitive terms to search in Teams and file names (review regularly).
-
Log your page counts and result volumes—silence can be a signal of a broken job.
Subscribe
For deeper dives and real-world Graph recipes, grab new episodes at m365.show/subscribe
Apple Podcasts · Spotify · YouTube
Host: Mirko Peters