<# .SYNOPSIS Analyze Exchange message flow statistics from transport logs .DESCRIPTION Aggregates statistics from Exchange transport logs including top senders/receivers, message volume by time period, and potential anomalies. Builds on Get-SMTPTraffic.ps1 pattern but provides comprehensive analysis. .PARAMETER LogPath Path to transport logs (default: auto-detect from Exchange install) .PARAMETER DaysBack Number of days of logs to analyze (default: 7) .PARAMETER OutputFolder Destination folder for reports. Default: .\MailflowStats- .PARAMETER TopCount Number of top senders/receivers to include in report (default: 25) .NOTES ⚠️ AI-GENERATED SCRIPT - UNTESTED This script was generated by Claude AI and has not been tested in production. Review and test thoroughly in a non-production environment before use. - Run in Exchange Management Shell - Can be slow with large log volumes - Analyzes SMTP Receive logs by default .EXAMPLE .\Get-MailflowStats.ps1 .EXAMPLE .\Get-MailflowStats.ps1 -DaysBack 30 -TopCount 50 #> [CmdletBinding()] param( [string]$LogPath = "", [int]$DaysBack = 7, [string]$OutputFolder = (Join-Path -Path (Get-Location) -ChildPath ("MailflowStats-" + (Get-Date -Format "yyyyMMdd-HHmm"))), [int]$TopCount = 25 ) function NowTag { (Get-Date).ToString("yyyy-MM-dd HH:mm:ss") } Write-Host "[$(NowTag)] ⚠️ AI-GENERATED SCRIPT - UNTESTED" -ForegroundColor Yellow Write-Host "[$(NowTag)] Starting mail flow analysis..." -ForegroundColor Green # Auto-detect log path if not specified if (-not $LogPath) { $exchangePath = (Get-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\ExchangeServer\v15\Setup" -ErrorAction SilentlyContinue).MsiInstallPath if ($exchangePath) { $LogPath = Join-Path $exchangePath "TransportRoles\Logs\FrontEnd\ProtocolLog\SmtpReceive" } else { Write-Host "[$(NowTag)] ERROR: Could not auto-detect Exchange log path. Please specify -LogPath parameter" -ForegroundColor Red exit 1 } } if (-not (Test-Path $LogPath)) { Write-Host "[$(NowTag)] ERROR: Log path not found: $LogPath" -ForegroundColor Red exit 1 } Write-Host "[$(NowTag)] Log path: $LogPath" # Create output folder New-Item -ItemType Directory -Path $OutputFolder -Force | Out-Null # Get log files within date range $cutoffDate = (Get-Date).AddDays(-$DaysBack) Write-Host "[$(NowTag)] Analyzing logs from $($cutoffDate.ToString('yyyy-MM-dd')) to $(Get-Date -Format 'yyyy-MM-dd')" $logFiles = Get-ChildItem -Path $LogPath -Filter "*.log" -ErrorAction SilentlyContinue | Where-Object { $_.LastWriteTime -ge $cutoffDate } | Sort-Object LastWriteTime -Descending $fileCount = ($logFiles | Measure-Object).Count if ($fileCount -eq 0) { Write-Host "[$(NowTag)] No log files found in specified date range" -ForegroundColor Yellow exit 0 } Write-Host "[$(NowTag)] Found $fileCount log file(s) to analyze" # Parse logs $allMessages = @() $fileIdx = 0 foreach ($logFile in $logFiles) { $fileIdx++ Write-Progress -Activity "Parsing Transport Logs" -Status "Processing $($logFile.Name) ($fileIdx/$fileCount)" -PercentComplete ([int](($fileIdx / $fileCount) * 100)) # Get header from file $header = Get-Content $logFile.FullName -TotalCount 50 | Where-Object { $_ -like '#Fields:*' } | Select-Object -First 1 if (-not $header) { continue } $fields = $header -replace '^#Fields: ', '' $columns = $fields -split ',' # Parse log entries Get-Content $logFile.FullName | Where-Object { -not ($_ -like '#*') -and $_ -match ',' } | ForEach-Object { try { $row = $_ -split ',(?=(?:[^"]*"[^"]*")*[^"]*$)' # Handle quoted fields $entry = [PSCustomObject]@{} for ($i = 0; $i -lt $columns.Count -and $i -lt $row.Count; $i++) { $entry | Add-Member -NotePropertyName $columns[$i].Trim() -NotePropertyValue ($row[$i] -replace '^"|"$', '') -Force } # Filter for relevant events (RECEIVE events) if ($entry.event -eq 'RECEIVE' -or $entry.event -eq '+') { $allMessages += $entry } } catch {} } } Write-Progress -Activity "Parsing Transport Logs" -Completed $totalMessages = $allMessages.Count Write-Host "[$(NowTag)] Parsed $totalMessages message(s)" if ($totalMessages -eq 0) { Write-Host "[$(NowTag)] No messages found to analyze" -ForegroundColor Yellow exit 0 } # Analyze data Write-Host "[$(NowTag)] Analyzing message flow patterns..." # Top senders (by remote-endpoint or client-ip) $topSenders = $allMessages | Where-Object { $_.'remote-endpoint' } | Group-Object -Property 'remote-endpoint' | Select-Object @{N = "IPAddress"; E = { $_.Name } }, Count | Sort-Object Count -Descending | Select-Object -First $TopCount # Messages by hour $messagesByHour = $allMessages | Where-Object { $_.'date-time' } | ForEach-Object { try { $dt = [DateTime]::Parse($_.'date-time') [PSCustomObject]@{ Hour = $dt.Hour } } catch {} } | Group-Object Hour | Select-Object @{N = "Hour"; E = { $_.Name } }, Count | Sort-Object Hour # Messages by day $messagesByDay = $allMessages | Where-Object { $_.'date-time' } | ForEach-Object { try { $dt = [DateTime]::Parse($_.'date-time') [PSCustomObject]@{ Date = $dt.ToString('yyyy-MM-dd') } } catch {} } | Group-Object Date | Select-Object @{N = "Date"; E = { $_.Name } }, Count | Sort-Object Date # Connector usage $connectorStats = $allMessages | Where-Object { $_.'connector-id' } | Group-Object -Property 'connector-id' | Select-Object @{N = "Connector"; E = { $_.Name } }, Count | Sort-Object Count -Descending # Export results Write-Host "[$(NowTag)] Exporting results..." $summaryFile = Join-Path $OutputFolder "Mailflow-Summary.txt" $topSendersFile = Join-Path $OutputFolder "Top-Senders-By-IP.csv" $byHourFile = Join-Path $OutputFolder "Messages-By-Hour.csv" $byDayFile = Join-Path $OutputFolder "Messages-By-Day.csv" $connectorFile = Join-Path $OutputFolder "Messages-By-Connector.csv" $topSenders | Export-Csv -NoTypeInformation -Encoding UTF8 -Path $topSendersFile $messagesByHour | Export-Csv -NoTypeInformation -Encoding UTF8 -Path $byHourFile $messagesByDay | Export-Csv -NoTypeInformation -Encoding UTF8 -Path $byDayFile $connectorStats | Export-Csv -NoTypeInformation -Encoding UTF8 -Path $connectorFile Write-Host "[$(NowTag)] Top senders: $topSendersFile" -ForegroundColor Green Write-Host "[$(NowTag)] Hourly distribution: $byHourFile" -ForegroundColor Green Write-Host "[$(NowTag)] Daily distribution: $byDayFile" -ForegroundColor Green Write-Host "[$(NowTag)] Connector stats: $connectorFile" -ForegroundColor Green # Summary report $summary = @" Mail Flow Statistics Report Generated: $(Get-Date) Analysis Period: $($cutoffDate.ToString('yyyy-MM-dd')) to $(Get-Date -Format 'yyyy-MM-dd') Log Path: $LogPath SUMMARY: Total Messages Analyzed: $totalMessages Log Files Processed: $fileCount Average Messages Per Day: $([math]::Round($totalMessages / $DaysBack, 0)) TOP $TopCount SENDERS (by IP): $($topSenders | ForEach-Object { " $($_.IPAddress): $($_.Count) messages" } | Out-String) BUSIEST HOURS (24-hour format): $($messagesByHour | Sort-Object Count -Descending | Select-Object -First 5 | ForEach-Object { " Hour $($_.Hour): $($_.Count) messages" } | Out-String) DAILY VOLUME: $($messagesByDay | ForEach-Object { " $($_.Date): $($_.Count) messages" } | Out-String) CONNECTOR USAGE: $($connectorStats | ForEach-Object { " $($_.Connector): $($_.Count) messages" } | Out-String) "@ $summary | Out-File -FilePath $summaryFile -Encoding UTF8 Write-Host "[$(NowTag)] Summary report: $summaryFile" -ForegroundColor Green # Console output Write-Host "`nMAIL FLOW SUMMARY:" -ForegroundColor Cyan Write-Host " Total Messages: $totalMessages" Write-Host " Average Per Day: $([math]::Round($totalMessages / $DaysBack, 0))" Write-Host " Peak Hour: $(($messagesByHour | Sort-Object Count -Descending | Select-Object -First 1).Hour):00" Write-Host " Busiest Day: $(($messagesByDay | Sort-Object Count -Descending | Select-Object -First 1).Date)" Write-Host "`n[$(NowTag)] Analysis complete! Output folder: $OutputFolder"