Microsoft 365 Reports¶
Usage and adoption reporting across the Microsoft 365 workloads: email and mailboxes, OneDrive and SharePoint, Teams, Microsoft 365 apps and Office activations, MFA coverage, and Copilot.
Every report comes from the Microsoft Graph reports API. Some endpoints return a JSON metadata object, but the usage numbers always arrive as CSV bytes that the scripts download, parse, and summarise.
How a usage report works¶
flowchart LR
C[GraphClient] -->|"reports.get_*_(period)"| R["Graph returns CSV bytes"]
R --> P["result.csv_rows()"]
P --> S["summarise / print / write to temp"]
One call, one CSV. execute_query() returns a ReportResult;
ReportResult.csv_rows() hands back one dict per row:
rows = client.reports.get_email_activity_counts("D30").execute_query().csv_rows()
for row in rows:
print(f"{row['Report Date'][:10]} sent={row['Send']} read={row['Read']}")
Decode with utf-8-sig, not utf-8
Graph prefixes the CSV with a UTF-8 BOM. Decoding with plain utf-8 leaves the
first column named "\ufeffReport Date", so every lookup silently returns
None. ReportResult.csv_rows() decodes with utf-8-sig for you.
Start here¶
Quickstart — tenant snapshot¶
Mail volume, mailbox count, OneDrive files and storage, and Teams in one pass —
the fastest way to confirm Reports.Read.All works and to get a feel for a tenant.
email = client.reports.get_email_activity_counts("D7").execute_query()
print(email.csv_rows()[-1]) # latest day: Send / Receive / Read
Any report¶
A generic explorer over all report methods: --list to discover, --report and
--period to fetch, --csv to save the raw CSV (defaults to the temp directory).
Export several reports concurrently¶
reports.download_report_async() streams each CSV, so independent reports overlap.
The download endpoint throttles aggressively, so the script caps concurrency and
retries on 429 with Retry-After.
The reports, by workload¶
| Folder | What it answers |
|---|---|
| How much mail flows, how big mailboxes are, who is near quota | |
| files | Who uses OneDrive/SharePoint, and where the storage lives |
| teams | How many teams exist and how active they are |
| apps | Which Microsoft 365 apps are used, and Office activation gaps |
| security | MFA / SSPR coverage, and admins without MFA |
| copilot | Copilot license adoption and usage |
Periods and permissions¶
Period-based reports take an aggregation window — D7, D30, D90, or D180
and the scripts default to D30 (the quickstart uses D7). All of them need the
Reports.Read.All application permission; the MFA report additionally accepts
AuditLog.Read.All.