Microsoft 365 Reports¶
Usage and adoption reports across the Microsoft 365 workloads — email, mailbox storage, OneDrive, SharePoint, Teams, Microsoft 365 apps, Office activations, MFA coverage, and Copilot.
Every report comes from the Microsoft Graph reports API, which returns CSV files the scripts download, parse, and summarize.
How every script works¶
flowchart LR
C[GraphClient] -->|"reports.get_*_counts(period)"| R["Graph returns CSV"]
R --> P["csv.DictReader"]
P --> S["Summarize / print"]
One call, one CSV. This is the whole pattern — the sections below each wrap it around a specific report:
def _parse_csv(result):
value = result.value
text = value.content.decode("utf-8") if hasattr(value, "content") else value.decode("utf-8")
return list(csv.DictReader(io.StringIO(text)))
data = client.reports.get_email_activity_counts("D30").execute_query()
for row in _parse_csv(data):
print(f"{row['Report Date'][:10]} sent={row['Send']} read={row['Read']}")
Email & mailbox¶
Email activity¶
Sent, received, and read counts per day — spot usage trends and plan mail licensing.
data = client.reports.get_email_activity_counts("D30").execute_query()
for row in _parse_csv(data):
print(f"{row['Report Date'][:10]} sent={row['Send']} received={row['Receive']} read={row['Read']}")
Mailbox storage¶
Tenant mailbox storage trend, so you can set quotas before users hit their limits.
data = client.reports.get_mailbox_usage_storage("D30").execute_query()
for row in _parse_csv(data):
gib = int(row["Storage Used (Byte)"]) / 1024**3
print(f"{row['Report Date'][:10]} {gib:.1f} GiB")
Files: OneDrive & SharePoint¶
OneDrive activity¶
Daily active users, files, synced users, and internal vs external sharing.
data = client.reports.get_onedrive_activity_user_counts("D30").execute_query()
for row in _parse_csv(data):
print(f"{row['Report Date'][:10]} active={row['Active Users']} files={row['Files']} synced={row['Synced Users']}")
SharePoint site usage¶
Storage per site — the basis for SharePoint storage governance and cleanup.
data = client.reports.get_sharepoint_site_usage_site_counts("D90").execute_query()
rows = sorted(_parse_csv(data), key=lambda r: float(r.get("Storage Used (Byte)") or 0), reverse=True)
for row in rows[:15]:
print(f"{row['Site Url']:55s} {float(row['Storage Used (Byte)']) / 1024**3:.1f} GiB")
Collaboration: Teams¶
Teams user activity¶
Per-user Teams activity — channel and chat posts, calls, and meetings.
data = client.reports.get_teams_user_activity_user_counts("D7").execute_query()
rows = _parse_csv(data)
posts = sum(int(r.get("Team Chat Message Count") or 0) for r in rows)
print(f"{len(rows)} active users, {posts} channel posts")
Apps & adoption¶
Microsoft 365 apps usage¶
Daily active users per Microsoft 365 app, including Copilot.
data = client.reports.get_m365_app_user_counts("D7").execute_query()
for row in _parse_csv(data):
print(row) # one row per app, with active-user counts
Office activations¶
Activated Office installs per product, across desktop, mobile, and web.
data = client.reports.get_office365_activation_counts().execute_query()
for row in _parse_csv(data):
print(f"{row['Product']:20s} total={row['Total']} activated={row['Is Activated']}")
Security: MFA coverage¶
MFA registration status¶
Who has MFA registered, which methods they use, and the tenant-wide coverage percentage.
result = client.reports.authentication_methods.user_registration_details.get().execute_query()
registered = sum(1 for d in result if d.is_mfa_registered)
print(f"{registered} of {len(result)} users registered ({registered / max(len(result), 1) * 100:.1f}%)")
for details in result:
print(f"{details.user_principal_name} mfa={details.is_mfa_registered} methods={details.properties.get('methods')}")
Copilot¶
License adoption¶
Which Copilot SKUs are subscribed, and how many licenses are consumed vs enabled.
skus = client.subscribed_skus.get().execute_query()
for sku in skus:
if "COPILOT" in (sku.sku_part_number or "").upper():
enabled = sku.prepaid_units.enabled if sku.prepaid_units else 0
print(f"{sku.sku_part_number:40s} consumed={sku.consumed_units} / enabled={enabled}")
Usage¶
Copilot activity, surfaced through the Microsoft 365 apps usage report.
data = client.reports.get_m365_app_user_counts("D7").execute_query()
for row in _parse_csv(data):
print(row) # Copilot appears alongside the other apps
Underused licenses¶
Users holding a Copilot license with no recent sign-in — candidates for license reclamation.
# 1. Collect Copilot SKU ids
sku_ids = {str(s.sku_id) for s in client.subscribed_skus.get().execute_query()
if "COPILOT" in (s.sku_part_number or "").upper()}
# 2. Users with a Copilot license who never signed in
users = client.users.select(["displayName", "userPrincipalName", "assignedLicenses", "signInActivity"]).get().execute_query()
for user in users:
licenses = {str(l.get("skuId")) for l in (user.properties.get("assignedLicenses") or [])}
if licenses & sku_ids and not user.properties.get("signInActivity"):
print(f"{user.user_principal_name} — Copilot license, no sign-in")
Download any report¶
Generic CSV downloader¶
One script that can fetch any report on this page — pass --report <name> --period <D7|D30|D90>.
data = client.reports.get_teams_user_activity_user_counts("D7").execute_query()
rows = _parse_csv(data)
for row in rows[:10]:
print(dict(row))