How to search exchange server logs recursively.
August 11, 2026 at 11:55 am Leave a comment
If we need to investigate a breach on the On-Prem Exchange server , the first course of action is to read the log files in the location X:\Program Files\Microsoft\Exchange Server\V15\Logging\.
The manual approach will be tedious as we need to open each file and look for an entry by an IP or other parameters. Instead of that you could use the below script to traverse all the log files and extract the presence of our search parameter (especially by an IP address) and produce the output as below(sometimes you may not see all the field populated) .We need to focus on the LogFile,LineNumber,Client IP .
| Time | Protocol | File | LineNumber | ClientIP | User | Uri | Status | UserAgent |
|---|---|---|---|---|---|---|---|---|
| 2026-08-10T03:13:59 | Autodiscover | …LOG | 4312 | 203.0.113.45 | user1 | /autodiscover/autodiscover.xml | 200 | … |
| 2026-08-10T03:14:22 | Mapi | …LOG | 15234 | 203.0.113.45 | user1 | /mapi/… | 200 | … |
| 2026-08-10T03:15:03 | Ews | …LOG | 8721 | 203.0.113.45 | user1 | /EWS/Exchange.asmx | 200 | … |
The sample script I used is (Source :ChatGPT)
$IP = “79.135.105.141”
$LogRoot = “C:\Program Files\Microsoft\Exchange Server\V15\Logging”
$Output = “C:\Temp\Exchange-IP-$($IP.Replace(‘.’,’_’)).csv”
#Create output directory if necessary
New-Item -ItemType Directory -Path (Split-Path $Output) -Force | Out-Null
$Results = foreach ($File in Get-ChildItem $LogRoot -Recurse -File) {
# Read the header to determine the Exchange log fields$FieldsLine = Get-Content $File.FullName -TotalCount 20 | Where-Object { $_ -match '^#Fields:' } | Select-Object -First 1if (-not $FieldsLine) { continue}$Fields = ($FieldsLine -replace '^#Fields:\s*', '').Split(',')# Find lines containing the IP$Matches = Select-String -Path $File.FullName -Pattern $IP -SimpleMatchforeach ($Match in $Matches) { $Values = $Match.Line.Split(',') $Record = [ordered]@{ Time = "" Protocol = $File.Directory.Name File = $File.Name LineNumber = $Match.LineNumber ClientIP = $IP User = "" Uri = "" Status = "" UserAgent = "" } for ($i = 0; $i -lt $Fields.Count -and $i -lt $Values.Count; $i++) { $Field = $Fields[$i].Trim() $Value = $Values[$i].Trim() switch -Regex ($Field) { '^date-time$|^timestamp$|^date$' { $Record.Time = $Value } 'client-ip|remote-ip|source-ip' { $Record.ClientIP = $Value } 'user|authenticated-user|user-name|username' { $Record.User = $Value } 'uri|url' { $Record.Uri = $Value } 'status|http-status' { $Record.Status = $Value } 'user-agent' { $Record.UserAgent = $Value } } } [PSCustomObject]$Record}
}
$Results |
Sort-Object Time |
Export-Csv -Path $Output -NoTypeInformation -Encoding UTF8
Write-Host “”
Write-Host “Search complete.”
Write-Host “Matches found: $($Results.Count)”
Write-Host “CSV: $Output”
Entry filed under: HOW To's. Tags: data-exfiltration, exchange-logs, forensic, on-prem.
Trackback this post | Subscribe to the comments via RSS Feed