Tools · Windows security and Event Logs · 12 / 27
Get-WinEvent tutorial: fast filtering with FilterHashtable and XPath
Query Windows event logs efficiently from PowerShell, extract fields from event XML, query remote machines, and avoid the slow Where-Object trap.
Filter at the source
Get-WinEvent -LogName Security | Where-Object Id -eq 4625 reads every event and then filters, which can take minutes. -FilterHashtable passes the filter to the event log service so only matching events are returned. Keys include LogName, Id (one or an array), ProviderName, Level, StartTime, EndTime, and Data for matching event data values.
-FilterXPath and -FilterXml allow more complex conditions, such as matching a specific field value, and are what Event Viewer’s custom views use under the hood.
Get-WinEvent -FilterHashtable @{LogName='Security'; Id=4625; StartTime=(Get-Date).AddDays(-1)} -MaxEvents 50
Get-WinEvent -LogName Security -FilterXPath "*[System[EventID=4624] and EventData[Data[@Name='LogonType']='10']]" -MaxEvents 20Getting fields out
Each event has a Properties array with values in a fixed order for that event ID, and a ToXml() method that returns named fields. Named fields from the XML are more readable and survive across versions; positional Properties are faster when you know the layout.
A small helper that converts an event into a PowerShell object with named properties makes every later query cleaner.
function ConvertFrom-WinEvent { process {
$x=[xml]$_.ToXml(); $o=[ordered]@{Time=$_.TimeCreated; Id=$_.Id; Computer=$_.MachineName}
foreach ($d in $x.Event.EventData.Data) { $o[$d.Name]=$d.'#text' }
[pscustomobject]$o } }
Get-WinEvent -FilterHashtable @{LogName='Security'; Id=4624} -MaxEvents 5 | ConvertFrom-WinEvent | Format-ListLab: measure the difference and go remote
Time the Where-Object version and the FilterHashtable version of the same query on a busy log with Measure-Command. Then query a second lab machine with -ComputerName, which needs the Remote Event Log Management firewall rule enabled, or wrap the query in Invoke-Command over WinRM.
Export results with Export-Csv for sharing, and use Get-WinEvent -Path to read a saved .evtx file collected from another machine.
Measure-Command { Get-WinEvent -LogName Security | Where-Object Id -eq 4625 }
Measure-Command { Get-WinEvent -FilterHashtable @{LogName='Security'; Id=4625} -ErrorAction SilentlyContinue }
Invoke-Command -ComputerName LAB-SRV01 { Get-WinEvent -FilterHashtable @{LogName='System'; Id=7045} -MaxEvents 10 }
Get-WinEvent -Path .\collected\Security.evtx -FilterXPath '*[System[EventID=1102]]'Practical tips
Use -ErrorAction SilentlyContinue when a filter may match nothing, otherwise PowerShell throws “No events were found.” Run as administrator to read the Security log. Prefer -MaxEvents while you are developing a query.
Get-WinEvent -ListLog * shows every log and its record count, and -ListProvider shows which provider writes to which log.
Diagram
Two ways to find 4625 events
Same answer, very different cost.
Command. Get-WinEvent -LogName Security | Where-Object Id -eq 4625
Work. Reads and parses every event.
Time. Slow on large logs.
Command. Get-WinEvent -FilterHashtable @{LogName='Security'; Id=4625}
Work. The log service returns matches only.
Time. Fast.
Check yourself
1. Why is FilterHashtable faster than Where-Object?
2. How do you read a saved .evtx file?
3. Which method returns named event fields such as TargetUserName?