oeltayeb.com · Exchange Server Troubleshooting Toolkit

Reference

Message tracking reference

Based on the article: Message tracking on Exchange Server: following an email with Get-MessageTrackingLog · 5 min read

"Did my email arrive?" is the most common mail flow question, and on-premises Exchange answers it with message tracking. Every transport server records what happened to each message it handled. In this post I'll show how to read those records, filter Get-MessageTrackingLog to find the right message fast, search all servers at once, and set location and retention so the history is there when you need it.

How message tracking works

The event IDs you'll read

EventIdWhat it tells you
RECEIVEThe message entered transport by SMTP or the Pickup/Replay directory (Source SMTP), or was submitted from a mailbox (Source STOREDRIVER).
SUBMITMailbox Transport Submission handed the message to the Transport service.
SENDSent by SMTP to the next hop: another Exchange server, a smart host or the internet.
DELIVERDelivered to a local mailbox.
DEFER / FAILDelivery delayed and will be retried / delivery failed.
DSNA delivery status notification, such as an NDR, was generated.
EXPAND, RESOLVE, REDIRECTA distribution group was expanded, a recipient resolved to another address, or mail went to an alternate recipient.
HAREDIRECT, HARECEIVEShadow redundancy copies were created and received; normal with several servers or a DAG.
AGENTINFOA transport agent logged its own data, for example a mail flow rule match.
DROPDropped without an NDR, for example a completed moderation request.
POISONMESSAGEMoved into or out of the poison message queue.

A routine message between two mailboxes on one server produces roughly this sequence: NOTIFYMAPI, RECEIVE (STOREDRIVER), SUBMIT, HAREDIRECT, RECEIVE (SMTP), AGENTINFO, SEND (SMTP) and finally DELIVER (STOREDRIVER).

Step-by-step: following one message

1. Start narrow on one server

powershell
Get-MessageTrackingLog -Server EX01 -Sender "chris@contoso.com" -Recipients "anna@fabrikam.com" `
  -Start (Get-Date).AddHours(-6) -End (Get-Date) -ResultSize Unlimited |
  Sort-Object Timestamp |
  Format-Table Timestamp, EventId, Source, ConnectorId, RecipientStatus -AutoSize

Relative times such as (Get-Date).AddHours(-6) avoid date-format surprises on servers with a non-US locale. Add -MessageSubject or -EventId to narrow further.

2. Take the MessageId and follow it across every server

The MessageId (the Message-ID header, angle brackets included) stays the same on every hop, while InternalMessageId differs per server. Search all transport servers and record which one logged each entry:

powershell
$msgId = "<4867a3d78a50438bad95c0f6d072fca5@ex01.contoso.com>"
Get-TransportService | ForEach-Object {
    $server = $_.Name
    Get-MessageTrackingLog -Server $server -MessageId $msgId -Start (Get-Date).AddDays(-3) -ResultSize Unlimited |
        Select-Object @{n='LoggedOn';e={$server}}, Timestamp, EventId, Source, ClientHostname, ServerHostname, ConnectorId, RecipientStatus
} | Sort-Object Timestamp | Format-Table -AutoSize

Edge Transport servers keep their own logs; if you have them, run the same search on the Edge server.

3. Hunt for failures and delays

powershell
Get-MessageTrackingLog -Server EX01 -EventId FAIL -Start (Get-Date).AddDays(-1) -ResultSize Unlimited |
  Format-Table Timestamp, Sender, Recipients, RecipientStatus -AutoSize

Get-MessageTrackingLog -Server EX01 -EventId DEFER -Start (Get-Date).AddHours(-4) -ResultSize Unlimited |
  Format-Table Timestamp, Sender, Recipients, RecipientStatus -AutoSize

RecipientStatus holds the SMTP status for each recipient, in the same order as Recipients, for example 250 2.1.5 Recipient OK or 550 4.4.7 QUEUE.Expired.

4. Export results for a ticket or spreadsheet

Recipients and RecipientStatus are multi-valued, so join them before exporting, or the CSV shows a type name instead of addresses:

powershell
Get-MessageTrackingLog -Server EX01 -Sender "chris@contoso.com" -Start (Get-Date).AddDays(-1) -ResultSize Unlimited |
  Select-Object Timestamp, EventId, Source, Sender,
    @{n='Recipients';e={$_.Recipients -join ';'}},
    @{n='RecipientStatus';e={$_.RecipientStatus -join ';'}},
    MessageSubject, MessageId, ConnectorId, TotalBytes |
  Export-Csv D:\Reports\tracking-chris.csv -NoTypeInformation

5. Prefer a GUI? Use delivery reports

In the Exchange admin center, Mail flow › Delivery reports searches the tracking logs for messages sent or received by one mailbox and presents the result in a readable form. It suits helpdesk staff; under the hood it uses Search-MessageTrackingReport and Get-MessageTrackingReport.

Verify: what "good" looks like

Tips & gotchas

References