M365Con 2027 – free online conference · 11–22 Jan 2027 · 120 sessions→ m365con.net

2025-08-05

Map Microsoft 365 Users, Groups, Files, and Shares with Graph

Map Microsoft 365 Users, Groups, Files, and Shares with Graph
Your “secure” files aren’t living on islands—they’re in a web of hidden links, group ripples, and quiet shares you’re not seeing. With Microsoft Graph Explorer, you can x-ray your tenant: hop from a user to every group, to every file, to every share, and finally to who actually touched the content.…

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)

  1. Anchor on the user
    GET /v1.0/users/{userPrincipalName}?$select=id,displayName,userPrincipalName

  2. Enumerate memberships
    GET /v1.0/users/{id}/memberOf?$select=id,displayName

  3. 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

  4. Pull item permissions and links
    GET /v1.0/drives/{driveId}/items/{itemId}/permissions?$select=id,grantedToV2,link,roles,shareId

  5. Trace who accessed what, when (where available)
    GET /v1.0/auditLogs/signIns or /security/alerts (pair with file, site, or sharing context)

  6. 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)

  1. User core → groups:
    /users/{upn}?$select=id,displayName
    /users/{id}/memberOf?$select=id,displayName

  2. For each M365 group:
    /groups/{groupId}/drive/root/children?$select=id,name,webUrl,lastModifiedDateTime&$top=200
    Follow @odata.nextLink

  3. For each item:
    /drives/{driveId}/items/{itemId}/permissions?$select=id,roles,grantedToV2,link
    Filter to link/scope in ('anonymous','organization')

  4. Export rows to your store (include tenantTime, siteId, driveId, itemId, webUrl, owner, scope, expires)

  5. 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

Related articles

← Build Better Phishing Reports with Defender and Power BIDetect Risky SharePoint and OneDrive External Sharing →