Not sure if this is technically a PowerShell question/issue. I have been using Get-WinEvent to query the Security log. My query takes a good deal of time and my issue is that it is VERY fast via an EventViewer query versus using Get-WinEvent.
Here is my XML query:
$XPath = "(Event[System[EventID=4656 or EventID=4659 or EventID=4660 or EventID=4663 or EventID=4691 or EventID=5038]]) and (Event[EventData[Data[@Name='SubjectUserSid'] != 'S-1-5-18']] and Event[EventData[Data[@Name='SubjectUserSid'] != 'S-1-5-19']] and Event[EventData[Data[@Name='ObjectName'] != 'Unknown']])"
If I use this query via EventViewer, it returns the results in literally seconds. However, using the same Query via Get-WinEvent takes minutes to complete.
Hmmm … I cannot reproduce this. Using your code as it is returns in a split second this:
Get-WinEvent : No events were found that match the specified selection criteria.
At line:1 char:1
+ Get-WinEvent -LogName 'Security' -FilterXPath $XPath
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : ObjectNotFound: (:) [Get-WinEvent], Exception
+ FullyQualifiedErrorId : NoMatchingEventsFound,Microsoft.PowerShell.Commands.GetWinEventCommand
But when I use less restritive criteria the results return immediately.
I can replicate this; it’s almost instant in Event Viewer but Get-WinEvent takes several seconds to return the results.
I’ve tried a few of the techniques that I suspect you’ve already come across: using -FilterHashtable, and using -ProviderName instead of -LogName, but they don’t seem to make much difference despite other people saying it worked for them.
In my testing, the only way I saw significant improvements was to limit the number of events returned using -MaxEvents which isn’t really a solution.
Olaf, it “might” be related to your advanced audit policy settings. You may not be gathering some of the events in the query.
Matt, I will play with MaxEvents. Thanks for the link.
In my testing, out of a total of about 200K total events in the security log, the query returned about 19K events. With EventViewer, it literally took maybe 3 or 4 seconds. With Get-WinEvent, it took a good 4 minutes (I know, I should use Measure-Object and will now ) which is significant. I was hoping for a .NET method that might get it done faster. If I come up with something significant, I will post.
I had the same experience as @Olaf - the filtered command gives me this: Get-WinEvent: No events were found that match the specified selection criteria.
When I took out the SID elements in the query it came back quickly.
Just guessing here about the performance difference. I suspect (I don’t know) that Event Viewer is not retrieving all the data for all of the logs and then filtering, but the filtering is built into the system, and the full details of the specific event you are viewing is returned when you view that event. Get-WinEvent would pull all the log details for every log entry it returns, which would be slower.
Something I would try is to see if that less restrictive filter is quicker. If it is, then I’d try doing less filtering on Get-WinEvent and then do some Where-Object filtering to cut through the list. It might be quicker, or it might not.
Thanks Darwin for the input. You lack of events returned would likely indicate your advanced audit policy settings are not gathering the events. I should have clarified that in my original post. I was not thinking clearly and should have realized for contributors to test, they would need to at least be gathering the events. I was hoping for either clarification that others see the same, or perhaps someone to show me the error of my ways. If you have a VM to play with, I can post the policy settings later today.
Well … I caved and asked AI for its thoughts on the issue. Basically, I asked for a .NET method to do the query. Sure enough, magnitudes faster. In my testing with 116K total events in the Security log and a return of ~9K events from the query, the .NET method took 2 seconds, the Get-WinEvent method took 49 seconds. Here is the code from AI, with some subtle changes (Measure-Command and adding to a Generic List) by me.
# Define the log name and query
$aryEvents = New-Object System.Collections.Generic.List[System.Object]
$logName = "Security"
$query = "(Event[System[EventID=4656 or EventID=4659 or EventID=4660 or EventID=4663 or EventID=4691 or EventID=5038]]) and (Event[EventData[Data[@Name='SubjectUserSid'] != 'S-1-5-18']] and Event[EventData[Data[@Name='SubjectUserSid'] != 'S-1-5-19']] and Event[EventData[Data[@Name='ObjectName'] != 'Unknown']])"
Measure-Command -Expression {
# Create an EventLogQuery object
$eventLogQuery = New-Object System.Diagnostics.Eventing.Reader.EventLogQuery($logName, [System.Diagnostics.Eventing.Reader.PathType]::LogName, $query)
# Create an EventLogReader
$eventLogReader = New-Object System.Diagnostics.Eventing.Reader.EventLogReader($eventLogQuery)
# Read and display events
while ($event = $eventLogReader.ReadEvent()) {
$aryEvents.Add($event)
}
}
Write-Output $aryEvents.Count
A bit more info on this which might explain why Get-WinEvent takes longer than the Event Viewer. Found this on StackOverflow:
As mentioned in the comments, Get-WinEvent uses the EventLogReader class to enumerate the events queried, and then calls EventRecord.FormatDescription() on each resulting record to render the localized message.
So basic .NET methods may be a ton faster, but they leave out the very important “Message” property that Get-WinEvent includes. Once you add that back in with:
$event.FormatDescription()
It pretty much slows down to being equal. So, if you don’t need the message/description, you can achieve vast performance improvements. In my case, I need that info so this has been a basic learning experience for me.