2025-08-05
Find Missing Microsoft 365 Compliance Data with Graph

Compliance Blindspots in M365: The Graph Endpoints Your Dashboards Don’t Show—and How to Pipe Them into Purview
Overview
Default compliance dashboards are summaries, not truth. Critical signals (DLP hits, eDiscovery actions, security alerts) often live behind Graph endpoints with separate permissions. We map the exact APIs, scopes, and scripting patterns to extract full-fidelity evidence and centralize it in Purview for clear, defensible reporting.
Who This Helps
-
Compliance/Legal/Risk owners who need audit-ready evidence
-
M365/Entra admins building reporting and automation
-
Security teams correlating DLP, eDiscovery, and alert telemetry
The Blindspots (why OOTB dashboards miss)
-
Aggregation & lag: UI summaries omit nested incident details and recent activity
-
Scope gaps: eDiscovery actions/DLP matches may be outside standard exports
-
Split permissions: /security & /compliance endpoints require different scopes
-
No joins: Dashboards rarely correlate incidents with users/custodians/case steps
The High-Value Graph Endpoints (copy-this list)
-
Security alerts (breadth)
-
Endpoint:
/security/alertsor/security/alerts_v2 -
App perms (examples):
SecurityEvents.Read.All(or Alerts.Read.All in newer models) -
Use for: Alert timelines, severity, status, assignments
-
-
DLP policies & activity (data loss)
-
Endpoints (tenant varies by availability):
-
/security/dataLossPreventionPolicies(policy inventory) -
/security/dlpIncidentsor advanced DLP incident endpoints in security namespace (tenant dependent)
-
-
App perms:
SecurityEvents.Read.All(and/or specific DLP read scopes as available) -
Use for: Matches, locations (Exchange/SharePoint/Teams), disposition
-
-
eDiscovery (cases & actions)
-
/compliance/ediscovery/cases -
Child paths:
/custodians,/searches,/reviewSets,/noncustodialDataSources,/operations -
App perms:
eDiscovery.Read.All(and write if managing cases) -
Use for: Case status, holds, custodians, exports, reviewer actions
-
-
Audit & sign-in context (supporting evidence)
-
/auditLogs/directoryAudits,/auditLogs/signIns -
App perms:
AuditLog.Read.All,Directory.Read.All -
Use for: Who changed what/when, session context
-
Tip: Many compliance payloads are deeply nested—plan to use
$expand, page via@odata.nextLink, and join with/users/{id}for display names and departments.
Auth & Permissions (zero-drama path)
-
Use Application permissions (service principal) for unattended jobs
-
Register separate apps for security vs. core Graph if your org requires split privilege
-
Consent only the least-privilege scopes; store secrets/certs in Key Vault
-
Implement exponential backoff for 429/503 and capture
x-ms-request-idin logs
Scripting Patterns (PowerShell or Python)
-
Select & expand: request only needed fields,
$select=…,$expand=…for nested entities -
Filter & time-box:
$filter=createdDateTime ge {ISO8601}for deltas -
Paginate: loop on
@odata.nextLink; never assume single-page results -
Normalize: map user/object IDs to names and departments up front for readable outputs
-
Join for context: enrich incidents with user, site, team, or case metadata before export
Wiring It Into Purview (turn data into decisions)
-
Standardize output: CSV/JSON with consistent field names (tenant, incidentId, workload, userId, userUPN, severity, status, created, lastAction, caseId)
-
Ingest: land files in a secured storage location Purview can read (e.g., ADLS Gen2)
-
Tag & label: apply sensitivity labels or custom tags (e.g., “DLP-PII-High”) for routing
-
Dashboards: Purview (or Power BI over Purview data) for trends, owners, SLA aging
-
Automations: rules to notify case owners on repeats/escalations; weekly PDF digests
30-Minute Pilot (step-by-step)
-
0–10 min: App registration +
SecurityEvents.Read.All/eDiscovery.Read.All; create secret/cert -
10–20 min: Script one endpoint (alerts or cases) with paging +
$filterfor last 7 days -
20–30 min: Enrich with
/users, export CSV, drop to ADLS path, visualize a basic chart
KPIs That Prove Value
-
% of incidents with owner & disposition populated
-
Mean time from incident → case (or reviewer)
-
Repeat-offender accounts/locations per 30 days
-
Delta vs. OOTB dashboard counts (gap closure)
-
Audit cycle time (evidence prep hours saved)
Common Pitfalls (and fixes)
-
Empty results → wrong scopes or using Delegated where Application is required
-
Missing detail → forgot
$expand/ nested collections not parsed -
Partial data → no pagination; only first page captured
-
Name mismatches → no enrichment join with
/usersor HR data -
Stale dashboards → no scheduled runs; add job + alerting on failures
Quick Wins This Week
-
Pull last 7 days from
/security/alertsand compare counts to the portal -
Export open eDiscovery cases + custodians; share with Legal for validation
-
Run a DLP incident sweep and tag repeats; open follow-ups in Purview
-
Schedule a weekly evidence bundle (CSV + PDF) to a compliance mailbox
FAQ
-
Do I need to be a developer? No—PowerShell or short Python scripts are enough.
-
Why app perms? Most /security and /compliance endpoints require headless, high-trust access.
-
Can I stay in PowerShell? Yes—use the Microsoft Graph PowerShell SDK and handle
$top,$filter,$expand, pagination. -
Is Purview required? Not strictly—but it turns raw JSON into auditable workflows and dashboards.
Conclusion
Dashboards summarize; Graph tells the whole story. Pull the right endpoints, fix auth, page and expand, then centralize in Purview. You’ll move from checkbox compliance to provable, real-time visibility—without drowning in manual exports.
Host: Mirko Peters