Skip to content

Commit fc5d66c

Browse files
Nickul-MSFTCopilot
andcommitted
Capture Get-MessageTraceV2 warnings as verbose output and update docs
- Replace 3>$null with 3>&1 stream redirection to capture warnings from Get-MessageTraceV2, then filter WarningRecord objects and surface them via Write-Verbose with page context - Update doc description to clarify this is an Exchange Online wrapper requiring an active EXO PowerShell session - Remove dot-sourcing from doc examples since the script is no longer a function Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent ea19d53 commit fc5d66c

2 files changed

Lines changed: 84 additions & 78 deletions

File tree

Lines changed: 80 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -1,98 +1,105 @@
11
# Copyright (c) Microsoft Corporation.
22
# Licensed under the MIT License.
33

4-
function Get-AllMessageTraceResults {
5-
[CmdletBinding()]
6-
[Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseOutputTypeCorrectly", "")]
7-
[OutputType([object[]])]
8-
param(
9-
[Parameter(Mandatory)]
10-
[DateTime]$StartDate,
4+
[CmdletBinding()]
5+
[Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseOutputTypeCorrectly", "")]
6+
[OutputType([object[]])]
7+
param(
8+
[Parameter(Mandatory)]
9+
[DateTime]$StartDate,
1110

12-
[Parameter(Mandatory)]
13-
[DateTime]$EndDate,
11+
[Parameter(Mandatory)]
12+
[DateTime]$EndDate,
1413

15-
[Parameter()]
16-
[string]$FromIP,
14+
[Parameter()]
15+
[string]$FromIP,
1716

18-
[Parameter()]
19-
[string[]]$MessageId,
17+
[Parameter()]
18+
[string[]]$MessageId,
2019

21-
[Parameter()]
22-
[guid]$MessageTraceId,
20+
[Parameter()]
21+
[guid]$MessageTraceId,
2322

24-
[Parameter()]
25-
[string[]]$RecipientAddress,
23+
[Parameter()]
24+
[string[]]$RecipientAddress,
2625

27-
[Parameter()]
28-
[string[]]$SenderAddress,
26+
[Parameter()]
27+
[string[]]$SenderAddress,
2928

30-
[Parameter()]
31-
[ValidateSet("Delivered", "Expanded", "Failed", "FilteredAsSpam", "GettingStatus", "Pending", "Quarantined")]
32-
[string[]]$Status,
29+
[Parameter()]
30+
[ValidateSet("Delivered", "Expanded", "Failed", "FilteredAsSpam", "GettingStatus", "Pending", "Quarantined")]
31+
[string[]]$Status,
3332

34-
[Parameter()]
35-
[string]$Subject,
33+
[Parameter()]
34+
[string]$Subject,
3635

37-
[Parameter()]
38-
[ValidateSet("Contains", "StartsWith", "EndsWith")]
39-
[string]$SubjectFilterType,
36+
[Parameter()]
37+
[ValidateSet("Contains", "StartsWith", "EndsWith")]
38+
[string]$SubjectFilterType,
4039

41-
[Parameter()]
42-
[string]$ToIP,
40+
[Parameter()]
41+
[string]$ToIP,
4342

44-
[Parameter()]
45-
[ValidateRange(1, 5000)]
46-
[int]$PageSize = 5000,
43+
[Parameter()]
44+
[ValidateRange(1, 5000)]
45+
[int]$PageSize = 5000,
4746

48-
[Parameter()]
49-
[ValidateRange(1, 2147483647)]
50-
[int]$TimeoutMinutes = 30
51-
)
47+
[Parameter()]
48+
[ValidateRange(1, 2147483647)]
49+
[int]$TimeoutMinutes = 30
50+
)
5251

53-
$timeout = (Get-Date).AddMinutes($TimeoutMinutes)
54-
$page = 1
55-
$allResults = [System.Collections.Generic.List[object]]::new()
52+
$timeout = (Get-Date).AddMinutes($TimeoutMinutes)
53+
$page = 1
54+
$allResults = [System.Collections.Generic.List[object]]::new()
5655

57-
# Build splat from bound parameters, excluding our custom ones
58-
$splatParams = @{}
59-
$excludeParams = @("TimeoutMinutes", "PageSize", "Verbose", "Debug", "ErrorAction", "WarningAction",
60-
"InformationAction", "ErrorVariable", "WarningVariable",
61-
"InformationVariable", "OutVariable", "OutBuffer", "PipelineVariable")
56+
# Build splat from bound parameters, excluding our custom ones
57+
$splatParams = @{}
58+
$excludeParams = @("TimeoutMinutes", "PageSize", "Verbose", "Debug", "ErrorAction", "WarningAction",
59+
"InformationAction", "ErrorVariable", "WarningVariable",
60+
"InformationVariable", "OutVariable", "OutBuffer", "PipelineVariable")
6261

63-
foreach ($key in $PSBoundParameters.Keys) {
64-
if ($key -notin $excludeParams) {
65-
$splatParams[$key] = $PSBoundParameters[$key]
66-
}
62+
foreach ($key in $PSBoundParameters.Keys) {
63+
if ($key -notin $excludeParams) {
64+
$splatParams[$key] = $PSBoundParameters[$key]
6765
}
66+
}
6867

69-
$splatParams["ResultSize"] = $PageSize
70-
71-
Write-Progress -Activity "Fetching message trace data" -Status "Page $page..."
72-
$results = Get-MessageTraceV2 @splatParams 3>$null
73-
if ($results) { $allResults.AddRange(@($results)) }
74-
75-
$timedOut = $false
76-
while ($results.Count -eq $PageSize) {
77-
if ((Get-Date) -ge $timeout) {
78-
$timedOut = $true
79-
break
80-
}
81-
$page++
82-
$lastResult = $results[-1]
83-
$splatParams["StartingRecipientAddress"] = $lastResult.RecipientAddress
84-
$splatParams["EndDate"] = $lastResult.Received
85-
Write-Progress -Activity "Fetching message trace data" -Status "Retrieved $($allResults.Count) messages (page $page)..."
86-
$results = Get-MessageTraceV2 @splatParams 3>$null
87-
if ($results) { $allResults.AddRange(@($results)) }
88-
}
68+
$splatParams["ResultSize"] = $PageSize
8969

90-
Write-Progress -Activity "Fetching message trace data" -Completed
70+
Write-Progress -Activity "Fetching message trace data" -Status "Page $page..."
71+
$rawResults = @(Get-MessageTraceV2 @splatParams 3>&1)
72+
$results = @($rawResults | Where-Object { $_ -isnot [System.Management.Automation.WarningRecord] })
73+
foreach ($w in ($rawResults | Where-Object { $_ -is [System.Management.Automation.WarningRecord] })) {
74+
Write-Verbose "Get-MessageTraceV2 warning (page $page): $w"
75+
}
76+
if ($results) { $allResults.AddRange($results) }
9177

92-
if ($timedOut) {
93-
Write-Warning "Timed out after $TimeoutMinutes minutes with more results available. Returning $($allResults.Count) results collected so far."
78+
$timedOut = $false
79+
while ($results.Count -eq $PageSize) {
80+
if ((Get-Date) -ge $timeout) {
81+
$timedOut = $true
82+
break
83+
}
84+
$page++
85+
$lastResult = $results[-1]
86+
$splatParams["StartingRecipientAddress"] = $lastResult.RecipientAddress
87+
$splatParams["EndDate"] = $lastResult.Received
88+
Write-Progress -Activity "Fetching message trace data" -Status "Retrieved $($allResults.Count) messages (page $page)..."
89+
$rawResults = @(Get-MessageTraceV2 @splatParams 3>&1)
90+
$results = @($rawResults | Where-Object { $_ -isnot [System.Management.Automation.WarningRecord] })
91+
foreach ($w in ($rawResults | Where-Object { $_ -is [System.Management.Automation.WarningRecord] })) {
92+
Write-Verbose "Get-MessageTraceV2 warning (page $page): $w"
9493
}
94+
if ($results) { $allResults.AddRange($results) }
95+
}
96+
97+
Write-Progress -Activity "Fetching message trace data" -Completed
9598

96-
Write-Verbose "Total messages retrieved: $($allResults.Count)"
97-
return $allResults
99+
if ($timedOut) {
100+
Write-Warning "Timed out after $TimeoutMinutes minutes with more results available. Returning $($allResults.Count) results collected so far."
98101
}
102+
103+
Write-Verbose "Total messages retrieved: $($allResults.Count)"
104+
return $allResults
105+

docs/Transport/Get-AllMessageTraceResults.md

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
Download the latest release: [Get-AllMessageTraceResults.ps1](https://github.com/microsoft/CSS-Exchange/releases/latest/download/Get-AllMessageTraceResults.ps1)
44

5-
This script provides a wrapper around `Get-MessageTraceV2` that automatically handles pagination to retrieve all matching message trace results from Exchange Online. It collects results in pages of up to 5000 and continues fetching until all results are returned or a timeout is reached.
5+
This script is a wrapper for the Exchange Online `Get-MessageTraceV2` cmdlet that automatically handles pagination. It requires an active Exchange Online PowerShell session. Results are collected in pages of up to 5000 and fetching continues until all results are returned or a timeout is reached.
66

77
## Parameters
88

@@ -63,20 +63,19 @@ The number of minutes before the script stops fetching additional pages. The def
6363
Retrieve all messages from the last 7 hours:
6464

6565
```powershell
66-
. .\Get-AllMessageTraceResults.ps1
67-
$messages = Get-AllMessageTraceResults -StartDate (Get-Date).AddHours(-7) -EndDate (Get-Date)
66+
$messages = .\Get-AllMessageTraceResults.ps1 -StartDate (Get-Date).AddHours(-7) -EndDate (Get-Date)
6867
```
6968

7069
Filter by sender and status:
7170

7271
```powershell
73-
$messages = Get-AllMessageTraceResults -StartDate (Get-Date).AddHours(-7) -EndDate (Get-Date) -SenderAddress "john@contoso.com" -Status "Delivered"
72+
$messages = .\Get-AllMessageTraceResults.ps1 -StartDate (Get-Date).AddHours(-7) -EndDate (Get-Date) -SenderAddress "john@contoso.com" -Status "Delivered"
7473
```
7574

7675
Filter by recipient with a custom page size:
7776

7877
```powershell
79-
$messages = Get-AllMessageTraceResults -StartDate (Get-Date).AddDays(-2) -EndDate (Get-Date) -RecipientAddress "jane@contoso.com" -PageSize 1000
78+
$messages = .\Get-AllMessageTraceResults.ps1 -StartDate (Get-Date).AddDays(-2) -EndDate (Get-Date) -RecipientAddress "jane@contoso.com" -PageSize 1000
8079
```
8180

8281
## Output

0 commit comments

Comments
 (0)