Aug. 11, 2026

Uncovering M365 Compliance Blindspots with Microsoft Graph

Welcome back to the podcast and our companion technical blog! If you have ever trusted an out-of-the-box Microsoft 365 compliance dashboard and assumed everything was fully accounted for, you might be walking into a false sense of security. Default compliance dashboards are designed to be summaries, not the absolute truth. Critical signals—such as Data Loss Prevention (DLP) hits, eDiscovery actions, and underlying security alerts—often live behind Microsoft Graph endpoints that require separate permissions, advanced query parameters, and proper data stitching. In this deep dive, we are going to explore why your native reports are missing crucial information, map out the exact Graph endpoints you need to query, and show you how to pipe that raw evidence directly into Microsoft Purview for clear, defensible reporting. To hear our complete discussion on this topic, be sure to check out the related podcast episode: Find Missing Microsoft 365 Compliance Data with Graph.

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/alerts or /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/dlpIncidents or 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-id in 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)

  1. Standardize output: CSV/JSON with consistent field names (tenant, incidentId, workload, userId, userUPN, severity, status, created, lastAction, caseId)

  2. Ingest: land files in a secured storage location Purview can read (e.g., ADLS Gen2)

  3. Tag & label: apply sensitivity labels or custom tags (e.g., “DLP-PII-High”) for routing

  4. Dashboards: Purview (or Power BI over Purview data) for trends, owners, SLA aging

  5. 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 + $filter for 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 /users or HR data

  • Stale dashboards → no scheduled runs; add job + alerting on failures

Quick Wins This Week

  • Pull last 7 days from /security/alerts and 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. For a deep-dive conversation and expert tips on tackling these blindspots directly, make sure to listen to our dedicated podcast episode Find Missing Microsoft 365 Compliance Data with Graph. Keep iterating on your scripts, secure your application permissions, and start surfacing the data your dashboards have been hiding!