Skip to content

About

Exchange Online message trace at scale through the Microsoft Graph API (v1.0): query planner that respects the tenant quota, local SQLite history beyond 90 days, CSV / JSON / HTML reports.

Topics

Resources

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

19 Commits

Folders and files

Repository files navigation

Message Trace Report: Exchange Online message trace at scale through the Microsoft Graph API, with a local SQLite history and CSV, JSON and HTML reports

Why  ·  How it works  ·  Before / after  ·  Reports  ·  Quick start  ·  User guide  ·  Developer guide

Important

Files downloaded from the Internet may be blocked by Windows and fail to run. Before using this project, unblock every file in the downloaded folder:

Get-ChildItem "C:\Chemin\Du\Dossier" -Recurse -File -Force | Unblock-File

Replace the example path with the folder where you downloaded or extracted this project.

Why

The Microsoft Graph message trace API traces Exchange Online messages without a PowerShell session — but within strict rules: 100 requests per 5 minutes for the whole tenant, windows of 10 days, 90 days of history. And one rule it does not document: in $filter it keeps one value per property and silently ignores or — a query for three recipients returns only the last one. This tool plans its requests around these rules, keeps everything it receives in a local SQLite database, and never asks twice for what it already holds.

Design principles: correct before fast, the quota is the limit, never twice, restartable, read-only and least privilege, one entry point

How it works

Filter and plan, only what is missing is read from Microsoft Graph at 90 requests per 5 minutes, stored page by page in SQLite, then the full filter is applied locally and the reports are written; four modes: Trace, Collect, Report, Status
  • One query per address of the shorter list (senders AND recipients), or per address (OR): the other conditions are applied on the database, so the report is exact. *@domain, subject, status, message ID and IP addresses are supported.
  • One shared rolling budget of 90 requests per 5 minutes (configurable), Retry-After honoured, token renewed, network errors retried — and the requests of the last 5 minutes are remembered by the next run.
  • Page by page into SQLite: Ctrl+C or a failure keeps what was received; the next run collects only the rest. A period already collected by the same or a wider query is not asked again; only the last hours, which may still change, are.
  • -Mode Collect every day keeps the whole tenant beyond the 90 days of Graph; -Mode Report answers from the database only, with any address pattern.
  • Why a message was not delivered: with -IncludeDetails the route of each recipient is read and explained — Blocked by DLP, Blocked by mail flow rule (ETR), Recipient not found, Mailbox full, Quarantined, Rejected by remote server… with the rule, the status code and its Microsoft Learn page, and where the route of a failed recipient left the route of the delivered ones.
  • Read-only, one permission (ExchangeMessageTrace.Read.All), certificate authentication built in — no module to install, SQLite bundled.

Before / after

Invoke-MessageTraceGraph.ps1, the script this tool replaces, against Message Trace Report on the same lab tenant:

1 sender AND 3 recipients over 7 days: 31.8 s and 1 recipient of 3 for the original script, 8.5 s and 3 recipients for Message Trace Report. 20 senders over 7 days: 17 min with 8,416 rows lost, 6 min 22 s with every row. The same again: 17 min, 27 s

Reports

HTML report overview
HTML report · tiles, delivery status, messages over time, top senders and recipients, why deliveries were not delivered
A message, its recipients and its route
A message · recipients grouped by route, where the routes split, the cause and the rule
A trace in the console
Console · plan, Microsoft Graph, collection with a live progress line, files
The route of one message in the console
One message · one recipient failed, the others delivered: why, and where the routes split

Each run writes Messages.csv (one row per message), Deliveries.csv (one row per message and recipient, what Graph returns, with the route, cause and reason when read), Routes.csv (one row per step of the routes read), Senders.csv and Recipients.csv (per day), optionally JSON with the Graph property names, and a self-contained HTML report that stays fast with 200,000 messages.

Requirements

Item Requirement
PowerShell 7.4 or later, Windows
Modules None (certificate or secret). Interactive sign-in: Microsoft.Graph.Authentication
Permission Microsoft Graph ExchangeMessageTrace.Read.All with admin consent
Tenant The service principal of the Microsoft application 8bd644d1-64a1-4d4b-ae52-2e0cbf64e373 (guide, chapter 7)
SQLite Bundled in lib\sqlite — nothing to install

Quick start

git clone https://github.com/Nico77600/MessageTraceReport.git
cd MessageTraceReport\package
notepad .\config\MessageTraceReport.config.psd1        # TenantId, AppId, CertificateThumbprint

.\Invoke-MessageTraceReport.ps1 -Sender john@contoso.com                          # last 48 hours
.\Invoke-MessageTraceReport.ps1 -Recipient *@fabrikam.com -Status Failed -Range Last7Days -Open
.\Invoke-MessageTraceReport.ps1 -SenderFile .\vip.csv -Recipient *@gmail.com -Start 2026-09-01 -End 2026-10-01
.\Invoke-MessageTraceReport.ps1 -Sender alerts@contoso.com -Recipient helpdesk@contoso.com -Operator Or -IncludeDetails
.\Invoke-MessageTraceReport.ps1 -MessageId '<CAJ1234@mail.contoso.com>' -Range Last10Days -IncludeDetails   # why each recipient got it or not
.\Invoke-MessageTraceReport.ps1 -Mode Collect                                     # scheduled task, whole tenant
.\Invoke-MessageTraceReport.ps1 -Mode Report -Range Last30Days -Sender 'sales-*@contoso.com'
.\Invoke-MessageTraceReport.ps1 -Mode Status

Coming from Invoke-MessageTraceGraph.ps1? Its parameter names are kept as aliases (-StartDate, -Senders, -RecipientsCsv …): see the migration table. The package folder of this repository holds exactly the files needed to run Message Trace Report, with both guides. The zip of each release contains the same run-time files with the HTML guides; .\tools\New-MtrPackage.ps1 builds that zip content from the repository.

Documentation

Guide Content
User guide What you need before the first trace, the one-time setup in the package folder or the extracted release, then one command per everyday question: a sender, a domain, a list of addresses, a message ID, why a recipient did not get it, the daily collection, a report from the database. Where the files are written, how to read the HTML report, the exit codes, and the situations that come back with what to do.
Developer guide Everything else: what the Graph API really does and the lab measurements, the performance figures, the prerequisites and the application registration, every configuration key, tracing and the routes in detail, the scheduled collection, the reports and their columns, exit codes and logs, the migration from the original script, the internals and how to test a change, troubleshooting and the Graph calls.

Both guides also exist as a single HTML file with a light and a dark theme (package/docs/MessageTraceReport-UserGuide.html, package/docs/MessageTraceReport-Guide.html): download them and open them locally, or use the copies in the release zip.

Tests

Invoke-Pester -Path .\tests          # Pester 5+, in-memory Graph API, no connection to Microsoft 365
.\tests\New-DemoReport.ps1 -Open     # a report of fictitious Contoso data, through the real engine

tools\Build-Documentation.ps1 rebuilds the HTML guides; tools\New-ReadmeImages.ps1 renders the graphics of this page from the guide, in a light and a dark version.

License

MIT. The bundled SQLite components keep their own licenses: see THIRD-PARTY-NOTICES.md.

Disclaimer

This Script is a Personal project. It's provided "AS-IS". It's not an official Microsoft product so no support can be expected from Microsoft.

As any scripts you must read carefully the documentation and test it first in a Test environment before any run in Production.

About

Exchange Online message trace at scale through the Microsoft Graph API (v1.0): query planner that respects the tenant quota, local SQLite history beyond 90 days, CSV / JSON / HTML reports.

Topics

Resources

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages