Files
PowerShell-scripts/Exchange/Get-MailflowStats.ps1
Martien de Kleijn 62134801aa Add 12 AI-generated PowerShell scripts with documentation
⚠️ IMPORTANT: These scripts are AI-GENERATED and UNTESTED

Exchange Scripts (5):
- Get-MailboxPermissions.ps1: Audit delegate access permissions
- Get-InactiveMailboxes.ps1: Identify stale mailboxes
- Compare-MailboxDatabases.ps1: Database health comparison
- Export-DistributionGroups.ps1: Distribution group inventory
- Get-MailflowStats.ps1: Transport log analysis

Active Directory Scripts (3):
- Get-ADUserLastLogon.ps1: True LastLogon across all DCs
- Export-OUStructure.ps1: OU hierarchy with GPO links
- Compare-ADGroupMemberships.ps1: Compare user group memberships

System Maintenance Scripts (4):
- Get-ServerInventory.ps1: Hardware/software inventory report
- Monitor-DiskSpace.ps1: Disk space monitoring with alerts
- Backup-ExchangeCertificates.ps1: Certificate backup to PFX
- Test-ExchangeHealth.ps1: Aggregated Exchange health checks

Documentation:
- Updated CLAUDE.md with AI-generated scripts section
- Added AI-GENERATED-SCRIPTS.md with warnings and testing guide

All scripts include prominent warnings and follow established patterns
from existing scripts. Require thorough testing before production use.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-15 10:52:44 +02:00

227 lines
8.1 KiB
PowerShell

<#
.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-<date>
.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"