Office 365: Get count of messages sent for user

Modified on Wed, 22 Jul at 5:42 PM

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-GridView


v2

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

Let us know how can we improve this article!

Select at least one of the reasons
CAPTCHA verification is required.

Feedback sent

We appreciate your effort and will try to fix the article