A Better Way To Search Events

I have recently put together a security script to use as an alerting system. Using a CSV file containing information on which users are assigned which computer, the event logs are searched to discover when a user signs into a device outside the norm.

In order to accomplish this task, the event logs need to be searched based on user and not necessarily on computer. I wanted to know how to search by computer as well. That is the task that led me here.

The best way to do this is using the Get-WinEvent cmdlet. This method is far superior to Get-EventLog in both speed and filtering ability. The documentation for the Filter Hash related parameters are a little lacking.


The FilterHashTable parameter is probably the most straight forward to use. I have taken the below example from Microsoft’s TechNet site. The format is easy to understand as we are searching for an array of properties. To accurately describe these properties, it is easiest to view the events in Event Viewer.

$StartTime = (Get-Date).AddDays(-7)
Get-WinEvent [email protected]{ Logname='Application'; ProviderName='Application Error'; Data='iexplore.exe'; StartTime=$StartTime }


If you have experience with the New-Object cmdlet you have most likely added properties to the newly created object in this same fashion. Looking at the list of available property queries below we are not able to search by computer name. I checked to see what other options were available. Data below is showing as an array. I have known this option to allow the entering of an SID or a username to query the event log. I have not been able to successfully add multiple properties into that value.

  • LogName=<String[]>
  • ProviderName=<String[]>
  • Path=<String[]>
  • Keywords=<Long[]>
  • ID=<Int32[]>
  • Level=<Int32[]>
  • StartTime=<DateTime>
  • EndTime=<DateTime>
  • UserID=<SID>
  • Data=<String[]>
  • (Asterisk) *=<String[]>


The next parameter defined is the FilterXPath parameter. This ended up being the one I used because it required less typing than FilterXML. More on this towards the end of the juicy stuff we are about to get into. To ensure the correct information is being used open the Event Viewer, (eventvwr.msc) and go to the XML view of the event you wish to query. In my case it was Security Event ID 4624.Below is a sample of the xml format for this event.

    <Provider Name="Microsoft-Windows-Security-Auditing" Guid="{####and Letters??????}" />
    <TimeCreated SystemTime="2019-08-28T00:01:10.833450200Z" />
    <Correlation />
    <Execution ProcessID="1132" ThreadID="504" />
    <Security />
    <Data Name="SubjectUserSid">S-1-5-0 ####</Data>
    <Data Name="SubjectUserName">david.haller</Data>
    <Data Name="SubjectDomainName">LEGION</Data>
    <Data Name="SubjectLogonId">0x3e7</Data>
    <Data Name="TargetUserSid">S-1-5-21-1005</Data>
    <Data Name="TargetUserName">david.haller</Data>
    <Data Name="TargetDomainName">LEGION</Data>
    <Data Name="TargetLogonId">0x33648</Data>
    <Data Name="LogonType">2</Data>
    <Data Name="LogonProcessName">I_Am_God</Data>
    <Data Name="AuthenticationPackageName">Negotiate</Data>
    <Data Name="WorkstationName">Why-Is-It-Blue</Data>
    <Data Name="LogonGuid">{00000000-0000-0000-0000-000000000000}</Data>
    <Data Name="TransmittedServices">-</Data>
    <Data Name="LmPackageName">-</Data>
    <Data Name="KeyLength">0</Data>
    <Data Name="ProcessId">0x210</Data>
    <Data Name="ProcessName">C:\Windows\System32\winlogon.exe</Data>
    <Data Name="IpAddress"></Data>
    <Data Name="IpPort">0</Data>
    <Data Name="ImpersonationLevel">%%1833</Data>

Microsoft has given us this syntax:-FilterXPath "*[System[Level=3 and TimeCreated[timediff(@SystemTime) <= 86400000]]]"

This starts with a wildcard character which represents anything before the <system> xml bracket. Each XML section is enclosed inside a set of [ ]. Starting small, If we wanted to query the ‘System’ section and the ‘EventData’ section it would look like the this.

*[System[] and EventData[]]


To define the properties we wish to search for we need to add those properties inside the set of brackets for System and or EventData. I am going to add on to what we have defined so far. The event id is an integer. Because it is an integer we do not want to add single quotes around the value.

*[System[EventID=4624] and EventData[]]


Adding to the “EventData” gets a little more tricky. In the XML format above you can see that a property has been defined for the XML tags and each tag is called Data. Following the format at this TechNet reference: https://docs.microsoft.com/en-us/previous-versions//aa385231(v=vs.85), we are able to view how to define this type of property. I placed a variable in the value field to demonstrate the need for single quotes as this is a string and single quotes are expected in order for the query to work.

$SamAccountName = 'Amahl.Farouk'
-FilterXPath "*[System[EventID=4624] and EventData[Data[@Name='TargetUserName']=`'$SamAccountName`']]


To add a second field to query the System section is fairly straight forward. To accomplish this we need to follow the last value with ‘and’ and add the new property as can be seen from the original Microsoft TechNet example. For those of you are unfamliar there are 86400000 seconds in 24 hours. So the time created value below gets the current system time and queries events that are less than or equal to a day old.

 *[System[EventID=4624 and TimeCreated[timediff(@SystemTime) &lt;= 86400000&lt;/span&gt;]] and EventData[Data[@Name='TargetUserName']='$SamAccountName']


Now We are searching for Event ID 4624, over the last 24 hours containing a specific username. Time to add the IP Address property. In the event log this value has an IP address and the computer’s name was not able to be found. I have a list of computer names so I will need to convert those names to IP addresses for my query to be successful. This meant for my script, that a Resolve-DnsName cmdlet had to be used to get the required value. There are numbers in this value but it is still not an integer so we are going to need single quotes around the value again.

"*[System[EventID=4624 and TimeCreated[timediff(@SystemTime) &lt;= 86400000]] and EventData[Data[@Name='TargetUserName']=`'$SamAccountName`'] and EventData[Data[@Name='IpAddress']=`'$IPv4Address`']]"


As you can see above, in order to successfully query the computer value and TargetUsername in the EventData XML tags we needed to add a second “and EventData”. This successfully finds what I was looking for.



FilterXML was another possible option that could have been used. In Microsoft’s TechNet Documentation, one of the examples they gave was as follows.

# Using the FilterXML parameter:
PS&gt; Get-WinEvent -FilterXML "&lt;QueryList&gt;&lt;Query&gt;&lt;Select Path='Windows PowerShell'&gt;*[System[Level=3 and TimeCreated[timediff(@SystemTime)&lt;= 86400000]]]&lt;/Select&gt;&lt;/Query&gt;&lt;/QueryList&gt;"

To query the Event Log using the FilterXML parameter we need to add the QueryList and Query tags on the outside of the defining properties we wish to filter by. Although here we used [ ] to separate fields we are also able to use normal xml tags. It is easiest to build an XML file of what we are looking for and querying that. The same sectioning rules apply  as before except FilterXML wants an XML formatted document when FilterXPath wants just the properties defined.The XPath 1.0 language Windows uses must resolve to Events not a single event. This seemed to make the most sense for my situation so it is what I went with.

Why would these two similar options be available for use you might question?!?!?! The Windows Event log does not fully support XPath query language. More information on this can be read HERE and HERE if interested. Windows Event Log uses a subset of XPath 1.0. There are specific limitations of XPath 1.0. The more options available the better the chance you are able to find what you are looking for using this cmdlet.

It is suggested to use XPath queries when you are searching the event logs for a simple expression from a single source. Use an XML structured query when you are searching from more than one event log source or you are using a compound expression with a dozen or more expressions.

Another example Microsoft gives for filtering events involves the Where-Object cmdlet. The overhead on Where-Object is fairly high so I try to avoid using it whenever I can as it will search through everything a second time and can noticeable slow down the execution time of a script. I hope you found this useful and were able to learn what I was able to through this. Until next time…


– tobor

About tobor79

Computer Systems Analyst: CCNA certified, a CompTIA Security Network Professional, CompTIA Secure Infrastructure Specialist, a Microsoft Certified Professional, as well as familiar with the Foundations of ITIL Service Management. I enjoy creating PowerShell Tools as well as somewhat simple web applications using .NET Core. I am interested in cyber security and will be going for an OSCP in the near future.