v1
Connect-ExchangeOnline
$UserEmails = @("user1@acmecorp.com")
$StartDate = (Get-Date).AddDays(-10).Date
$EndDate = Get-Date
$finalReport = foreach ($Email in $UserEmails) {
# 1. Fetch Sent Messages
$sentTraces = Get-MessageTraceV2 -SenderAddress $Email -StartDate $StartDate -EndDate $EndDate -ResultSize 5000 |
Select-Object MessageId, Received, @{Name='Direction'; Expression={'Sent'}}
# 2. Fetch Received Messages
$receivedTraces = Get-MessageTraceV2 -RecipientAddress $Email -StartDate $StartDate -EndDate $EndDate -ResultSize 5000 |
Select-Object MessageId, Received, @{Name='Direction'; Expression={'Received'}}
# Combine both datasets
$allTraces = $sentTraces + $receivedTraces
# Deduplicate based on MessageId AND Direction (handles internal emails to self correctly)
$uniqueEmails = $allTraces | Group-Object MessageId, Direction | ForEach-Object {
$firstEntry = $_.Group[0]
[PSCustomObject]@{
Direction = $firstEntry.Direction
Date = $firstEntry.Received.Date
}
}
# Group strictly by Date to combine Sent and Received into a single row
$uniqueEmails | Group-Object Date | ForEach-Object {
# Filter the group's internal items to count Sent vs Received
$sentCount = ($_.Group | Where-Object { $_.Direction -eq 'Sent' }).Count
$receivedCount = ($_.Group | Where-Object { $_.Direction -eq 'Received' }).Count
[PSCustomObject]@{
UserAddress = $Email
Date = [datetime]$_.Name
SentCount = if ($sentCount) { $sentCount } else { 0 }
ReceivedCount = if ($receivedCount) { $receivedCount } else { 0 }
}
}
}
# Output the flattened results
$finalReport | Sort-Object Date, UserAddress | Out-GridViewv2
Connect-ExchangeOnline
# ---------------------------------------------------------
# 1. Multi-line Console Input for User Emails
# ---------------------------------------------------------
Write-Host "Paste or type your list of emails below (one per line, comma, or space-separated)." -ForegroundColor Cyan
Write-Host "Press [ENTER] on a blank line when you are finished:" -ForegroundColor Yellow
$rawLines = @()
while ($true) {
$line = Read-Host
if ($line -eq "") { break }
$rawLines += $line
}
$userInput = $rawLines -join "`n"
$UserEmails = $userInput -split "[\r\n,\s]+" | Where-Object { ![string]::IsNullOrWhiteSpace($_) } | ForEach-Object { $_.Trim() }
if (-not $UserEmails) {
Write-Warning "No users provided. Exiting..."
exit
}
Write-Host "Loaded $($UserEmails.Count) email address(es)." -ForegroundColor Green
# ---------------------------------------------------------
# 2. Native Input for Days Back (Fixed)
# ---------------------------------------------------------
$daysInput = Read-Host -Prompt "Enter number of days back to search (Default: 14)"
if ([string]::IsNullOrWhiteSpace($daysInput)) { $daysInput = "14" }
try {
$DaysBack = [int]$daysInput
} catch {
Write-Warning "Invalid number of days provided. Please enter a whole number. Exiting..."
exit
}
# ---------------------------------------------------------
# 3. Calculate Date Chunks & Master Date Columns
# ---------------------------------------------------------
$dateRanges = @()
$currentEndDate = (Get-Date)
$finalStartDate = $currentEndDate.AddDays(-$DaysBack).Date
$tempEndDate = $currentEndDate
# Build the 10-day chunks for the API
while ($tempEndDate -gt $finalStartDate) {
$tempStartDate = $tempEndDate.AddDays(-10)
if ($tempStartDate -lt $finalStartDate) { $tempStartDate = $finalStartDate }
$dateRanges += [PSCustomObject]@{ Start = $tempStartDate; End = $tempEndDate }
$tempEndDate = $tempStartDate
}
# Generate an array of string dates to serve as our column headers (Newest to Oldest)
$masterDates = @()
$dateIterator = $currentEndDate.Date
while ($dateIterator -ge $finalStartDate) {
$masterDates += $dateIterator.ToString('yyyy-MM-dd')
$dateIterator = $dateIterator.AddDays(-1) # Subtract a day to go backwards
}
# ---------------------------------------------------------
# 4. Fetch, Process, and Pivot Traces
# ---------------------------------------------------------
$finalReport = foreach ($Email in $UserEmails) {
$allTraces = @()
# Fetch all chunks
foreach ($range in $dateRanges) {
$StartDate = $range.Start
$EndDate = $range.End
Write-Host "Fetching traces for $Email from $($StartDate.ToString('yyyy-MM-dd')) to $($EndDate.ToString('yyyy-MM-dd'))..." -ForegroundColor Cyan
$sentTraces = Get-MessageTraceV2 -SenderAddress $Email -StartDate $StartDate -EndDate $EndDate -ResultSize 5000 |
Select-Object MessageId, Received, @{Name='Direction'; Expression={'Sent'}}
$receivedTraces = Get-MessageTraceV2 -RecipientAddress $Email -StartDate $StartDate -EndDate $EndDate -ResultSize 5000 |
Select-Object MessageId, Received, @{Name='Direction'; Expression={'Received'}}
if ($sentTraces) { $allTraces += $sentTraces }
if ($receivedTraces) { $allTraces += $receivedTraces }
}
# Deduplicate and format dates to match our column headers
$uniqueEmails = $allTraces | Group-Object MessageId, Direction | ForEach-Object {
$firstEntry = $_.Group[0]
[PSCustomObject]@{
Direction = $firstEntry.Direction
DateStr = $firstEntry.Received.Date.ToString('yyyy-MM-dd')
}
}
# Group by the date string and store the formatted count in a hash table
$dateCounts = @{}
$uniqueEmails | Group-Object DateStr | ForEach-Object {
$sentCount = ($_.Group | Where-Object { $_.Direction -eq 'Sent' }).Count
$receivedCount = ($_.Group | Where-Object { $_.Direction -eq 'Received' }).Count
$dateCounts[$_.Name] = "$sentCount / $receivedCount"
}
# Build a dynamic row (ordered dictionary) for this user
$row = [ordered]@{ UserAddress = $Email }
# Because $masterDates is newest-first, the columns will populate newest-first
foreach ($d in $masterDates) {
if ($dateCounts.Contains($d)) {
$row[$d] = $dateCounts[$d]
} else {
$row[$d] = "0 / 0"
}
}
# Cast the ordered dictionary to a custom object to finalize the row
[PSCustomObject]$row
}
# ---------------------------------------------------------
# 5. Output the Pivoted Results
# ---------------------------------------------------------
if ($finalReport) {
$finalReport | Out-GridView -Title "Message Trace Summary (Sent / Received) - Newest to Oldest"
} else {
Write-Host "No data generated." -ForegroundColor Yellow
}Was this article helpful?
That’s Great!
Thank you for your feedback
Sorry! We couldn't be helpful
Thank you for your feedback
Feedback sent
We appreciate your effort and will try to fix the article