Tuesday, August 25, 2026

Fast Event Log Query with FilterXPath

I've been working with Powershell for way too long to have just figured this out. However, here we are...

To query event log information from a remote server, you can use Get-WinEvent. However, I've always found it really slow and not very flexible.

Recently I needed to get events from the Security log related to NTLM v1 authentication and there were about 200,000 events in the security log. The event ID that I needed to evaluate was about half of that. So, I needed to query 100,000 events.

When I used the -FilterHashTable parameter, it took about 10 minutes. That's not a good time. And processing the results also took about 10 minutes.

Many events were not relevant, because I only needed events that referred to NTLM v1 authentication. This reference was in the message and I was doing text processing on the message attribute to find the relevant events.

If you use -FilterXPath, you can refer to items in the message as an attribute and filter based on them. This got my query from 10 minutes down to less than a minute. You can view the attributes available for filtering in the properties of an event in the XML view. What appears as text in the message shows up as specific data names in EventData that you can filter on.

 

 Here is an example of the code:

#Query only events with NTLM V1 (1 min or less per DC)
$comp = "XXXXXXX"
$xpath = "*[System[EventID=4624] and EventData[Data[@Name='LMPackageName']='NTLM V1']]"
$LogonEvents = Get-WinEvent -ComputerName $comp -Credential $cred -FilterXPath $xpath -LogName Security

The filter I used looked for EventID=4624 in the System section of the event. In the EventData portion of the event, it looked for LMPackageName='NTLM v1'. Normally LMPackageName appears in the message portion of the event and it's not filterable. Shown below as Package Name (NTLM only) with a value of -.

So, -FilterXPath is more flexible for filtering which significantly reduces the number of events transferred by Get-WinEvent and provides a huge increase in speed.

 

Audit NTLM v1 on DC

Windows Server 2025 no longer supports NTLM v1 (which shouldn't exist on your network). However, just to be safe before updating domain controllers, we should verify.

The following script queries relevant events from the security log (fast using FilterXPath) and groups them.

If all of the events returned are for ANONYMOUS LOGON then you can ignore them. The logging logic in Windows gives false positives for NTLM v1 with anonymous authentication. When it doesn't see NTLM v2, it logs it as NTLM v1.

More info here: https://learn.microsoft.com/en-us/troubleshoot/windows-server/windows-security/audit-domain-controller-ntlmv1

$cred = Get-Credential

#Query only events with NTLM V1 (1 min or less per DC)
$comp = "XXXXXXX"
$xpath = "*[System[EventID=4624] and EventData[Data[@Name='LMPackageName']='NTLM V1']]"
$LogonEvents = Get-WinEvent -ComputerName $comp -Credential $cred -FilterXPath $xpath -LogName Security

foreach ($event in $LogonEvents) {

    $lines = $event.message -split '\r?\n'
    $AcctLine = $lines | where {$_ -like "*Account Name:*"} 
    $Acct = ($AcctLine[1] -split ":")[1].trim()
    $event | Add-Member -NotePropertyName Account -NotePropertyValue $Acct -force


    $NTLM = $lines | where {$_ -like "*Package Name (NTLM only):*"} 
    $NTLMVersion = ($NTLM -split ":")[1].trim()
    $event | Add-Member -NotePropertyName NTLMVersion -NotePropertyValue $NTLMVersion -force

    $WS = $lines | where {$_ -like "*Workstation Name:*"} 
    $WSName = ($WS -split ":")[1].trim()
    $event | Add-Member -NotePropertyName Workstation -NotePropertyValue $WSName -force

} #end foreach

#Make sure only NTLM v1
$LogonEvents | Group-Object -Property NTLMVersion

#Can ignore ANONYMOUS LOGON
$logonevents | where NTLMVersion -eq "NTLM V1" | Group-Object Account

#In case you care which computers they're coming from
$LogonEvents | Group-Object Workstation