Public/MailFlow/Trace-Message.ps1
Function Trace-Message { <# .SYNOPSIS Search message trace logs in Exchange Online by hour or partial hour start and end times If desired, one or more messages can be selected from the results for more detail .DESCRIPTION Search message trace logs in Exchange Online by hour or partial hour start and end times If desired, one or more messages can be selected from the results for more detail Just click OK once you have selected the message(s) Many thanks to Matt Marchese for the initial framework of this function .PARAMETER SenderAddress Senders Email Address .PARAMETER RecipientAddress Recipients Email Address .PARAMETER StartSearchHoursAgo Number of hours from today to start the search. Default is (.25) 15 minutes ago .PARAMETER EndSearchHoursAgo Number of hours from today to end the search. "Now" is the default, the number "0" .PARAMETER Subject Partial or full subject of message(s) of which are being searched .PARAMETER FromIP The IP address from which the email originated .PARAMETER ToIP The IP address to which the email was destined .PARAMETER Status The Status parameter filters the results by the delivery status of the message. Valid values for this parameter are: None: The message has no delivery status because it was rejected or redirected to a different recipient. Failed: Message delivery was attempted and it failed or the message was filtered as spam or malware, or by transport rules. Pending: Message delivery is underway or was deferred and is being retried. Delivered: The message was delivered to its destination. Expanded: There was no message delivery because the message was addressed to a distribution group, and the membership of the distribution was expanded. .EXAMPLE Trace-Message .EXAMPLE Trace-Message -StartSearchHoursAgo 10 -EndSearchHoursAgo 5 -Subject "arizona" This will find all messages with the word "arizona" somewhere in the subject, that were sent or received anywhere from 10 hours ago till 5 hours ago .EXAMPLE Trace-Message -StartSearchHoursAgo 10 -EndSearchHoursAgo 5 -Subject "Letter from the CEO" .EXAMPLE Trace-Message -SenderAddress "User@domain.com" -RecipientAddress "recipient@domain.com" -StartSearchHoursAgo 15 -FromIP "xx.xx.xx.xx" #> [CmdletBinding()] [Alias('New-EXOMessageTrace')] param ( [Parameter()] [string] $SenderAddress, [Parameter()] [string] $RecipientAddress, [Parameter()] [Double] $StartSearchHoursAgo = ".25", [Parameter()] [Double] $EndSearchHoursAgo = "0", [Parameter()] [string] $Subject, [Parameter()] [string] $FromIP, [Parameter()] [string] $ToIP, [Parameter()] [string] $Status ) $currentErrorActionPrefs = $ErrorActionPreference $ErrorActionPreference = 'Stop' if ($StartSearchHoursAgo) { [DateTime]$StartSearchHoursAgo = ((Get-Date).AddHours( - $StartSearchHoursAgo)) $StartSearchHoursAgo = $StartSearchHoursAgo.ToUniversalTime() } if ($StartSearchHoursAgo) { [DateTime]$EndSearchHoursAgo = ((Get-Date).AddHours( - $EndSearchHoursAgo)) $EndSearchHoursAgo = $EndSearchHoursAgo.ToUniversalTime() } $cmdletParams = (Get-Command $PSCmdlet.MyInvocation.InvocationName).Parameters.Keys $params = @{ } $NotArray = 'StartSearchHoursAgo', 'EndSearchHoursAgo', 'Subject', 'Debug', 'Verbose', 'ErrorAction', 'WarningAction', 'InformationAction', 'ErrorVariable', 'WarningVariable', 'InformationVariable', 'OutVariable', 'OutBuffer', 'PipelineVariable' foreach ($cmdletParam in $cmdletParams) { if ($cmdletParam -notin $NotArray) { if ([string]::IsNullOrWhiteSpace((Get-Variable -Name $cmdletParam).Value) -ne $true) { $params.Add($cmdletParam, (Get-Variable -Name $cmdletParam).Value) } } } $params.Add('StartDate', $StartSearchHoursAgo) $params.Add('EndDate', $EndSearchHoursAgo) $counter = 1 $continue = $false $allMessageTraceResults = [System.Collections.Generic.List[PSObject]]::New() do { Write-Verbose "Checking message trace results on page $counter." try { if (-not $Subject) { $messageTrace = Get-MessageTrace @params -Page $counter if ($messageTrace) { $messageTrace | ForEach-Object { $messageTraceResults = [PSCustomObject]@{ Received = $_.Received Status = $_.Status SenderAddress = $_.SenderAddress RecipientAddress = $_.RecipientAddress Subject = $_.Subject FromIP = $_.FromIP ToIP = $_.ToIP MessageTraceId = $_.MessageTraceId MessageId = $_.MessageId } $allMessageTraceResults.Add($messageTraceResults) } $counter++ Start-Sleep -Seconds 2 } else { Write-Verbose "`tNo results found on page $counter." $continue = $true } } else { $messageTrace = Get-MessageTrace @params -Page $counter $messageTracewithSubject = "" $messageTracewithSubject = $messageTrace | Where-Object { $_.Subject -like "*$Subject*" } if ($messageTracewithSubject) { $messageTracewithSubject | ForEach-Object { $messageTraceResults = [PSCustomObject]@{ Received = $_.Received Status = $_.Status SenderAddress = $_.SenderAddress RecipientAddress = $_.RecipientAddress Subject = $_.Subject FromIP = $_.FromIP ToIP = $_.ToIP MessageTraceId = $_.MessageTraceId MessageId = $_.MessageId } $allMessageTraceResults.Add($messageTraceResults) } } if (-not $messageTrace) { Write-Verbose "`tNo results found on page $counter." $continue = $true } else { $counter++ Start-Sleep -Seconds 2 } } } catch { Write-Verbose "`tException gathering message trace data on page $counter. Trying again in 30 seconds." Start-Sleep -Seconds 30 } } while ($continue -eq $false) if ($allMessageTraceResults.count -gt 0) { Write-Verbose "`n$($allMessageTraceResults.count) results returned." $WantsDetailOnTheseMessages = $allMessageTraceResults | Out-GridView -PassThru -Title "Message Trace Results. Select one or more then click OK for detailed report." if ($WantsDetailOnTheseMessages) { Foreach ($Wants in $WantsDetailOnTheseMessages) { $Splat = @{ MessageTraceID = $Wants.MessageTraceID RecipientAddress = $Wants.RecipientAddress MessageId = $Wants.MessageId } $TraceDetail = Get-MessageTraceDetail @Splat $TraceDetail | Select-Object Date, Event, Action, Detail, Data, MessageTraceID, MessageID | Out-GridView -Title "DATE: $($Wants.Received) STATUS: $($Wants.Status) FROM: $($Wants.SenderAddress) TO: $($Wants.RecipientAddress) TRACEID: $($Wants.MessageTraceId)" } } } else { Write-Verbose "`nNo Results found." } $ErrorActionPreference = $currentErrorActionPrefs } |