Auditing system events can be construed as a daunting, tedious, and intimidating task. The enablement of advanced audit policy configuration is often necessary to log the successes and failures required to identify unauthorized and malicious activity. Configuring a system accordingly results in numerous events, many of which may very well be the outcome of everyday normal activity. So how does one manage?

If used properly, advanced filtering within the Windows Event Viewer could assist, but an in-depth review could take hours. Worse, because so many events are logged, unauthorized and/or malicious activity could go unnoticed.

Windows PowerShell presents unique functionality that can assist with resolving this complex issue and can be used to audit local and remote systems effectively and efficiently.

Windows Event Log Structure (EVTX)

Microsoft introduced their EVTX logging format in Windows Vista and Server 2008. Their release was accompanied by numerous new features, one of which modified formatting requirements to use Extensible Markup Language (XML). The use of XML facilitates a structured approach to event management and enhances event property filtering and querying capabilities. The following screenshot shows the XML view for Windows event 4624 An account was successfully logged on:


Introduction to Windows PowerShell

Windows PowerShell is a command-line shell that leverages .NET Framework to promote, among other things, task automation and configuration management. Built-in commands, known as cmdlets (pronounced Command-Lets), take the form of verb-noun (example: Get-Process) and offer an easily understood and user-friendly experience.

Auditing with Windows PowerShell

Relevant Cmdlets

Two cmdlets within PowerShell version 5.1 function with the primary purpose of querying events of interest from the Event Log on local and remote computers:

1. Get-EventLog: This cmdlet pulls the events from an event log, or a list of the event logs, on local and remote computers. Get-EventLog uses a Win32 API that is deprecated, which could lead to inaccurate results. For this reason, the Get-EventLog cmdlet will not be discussed within this post.

2. Get-WinEvent: This cmdlet pulls events from event logs, including classic logs, such as the System and Application logs, and the event logs that are generated by the Windows Event Log technology introduced in Windows Vista. It also pulls events in log files generated by Event Tracing for Windows (ETW).

The Get-WinEvent cmdlet allows system auditors to use parameters to direct the cmdlet. We will be using the ComputerName and FilterHashtable parameters within our custom functions. Below are parameter-relevant details:

ComputerName <String>

  • Gets events from the event logs on the specified computer. Type the NetBIOS name, an Internet Protocol (IP) address, or the fully qualified domain name of the computer. The default value is the local computer.
  • This parameter accepts only one computer name at a time. To find event logs or events on multiple computers, use a ForEach statement. For more information about this parameter, see the examples.
  • To get events and event logs from remote computers, the firewall port for the event log service must be configured to allow remote access.
  • This cmdlet does not rely on Windows PowerShell remoting. You can use the ComputerName parameter even if your computer is not configured to run remote commands.

FilterHashtable <Hastable[]>

  • Uses a query in hash table format to select events from one or more event logs. The query contains a hash table with one or more key-value pairs.
    • Keys and values are case-insensitive.
    • Wildcard characters are valid only in the values associated with the LogName and ProviderName keys.
    • Each key can be listed only once in each hash-table.
    • The Path value takes paths to .etl, .evt, and .evtx log files.
    • The LogName, Path, and ProviderName keys can be used in the same query.
    • The UserID key can take a valid security identifier (SID) or a domain account name that can be used to construct a valid System.Security.Principal.NTAccount object.
    • The Data value takes event data in an unnamed field. This is for events in classic event logs.
    • The * key represents a named event data field.Hash table queries have the following rules:
  • When Get-WinEvent cannot interpret a key-value pair, it interprets the key as a case-sensitive name for the event data in the event. The valid key-value pairs are as follows:
      • LogName=<String[]>
      • ProviderName=<String[]>
      • Path=<String[]>
      • Keywords=<Long[]>
      • ID=<Int32[]>
      • Level=<Int32[]
      • StartTime=<DateTime>
      • EndTime=<DataTime>
      • UserID=<SID>
      • Data=<String[]>
      • *=<String[]>

Filtering With Get-WinEvent

As stated above, the Get-WinEvent Cmdlet allows system auditors the ability to customize advanced filtering options. The following simple one line command will query the Security Log for Event ID 4624 An account was successfully logged on:

Get-WinEvent -FilterHashtable @{LogName="Security";ID=4624}

By default, the response is truncated, displaying a default set of properties so that it fits nicely into the window. Expanding the response can be accomplished using the Format-Table Cmdlet’s -Wrap parameter:

Get-WinEvent -FilterHashtable @{LogName="Security";ID=4624} | Format-Table -Wrap

As stated above, the cmdlet displays a default set of properties unless otherwise specified. Let’s look at what additional properties are available using the Get-Member Cmdlet:

Get-WinEvent -FilterHashtable @{LogName="Security";ID=4624} -MaxEvents 1 | Get-Member -MemberType Properties

As you can see, there are numerous properties that are not displayed by default. These properties can be accessed using the Select-Object Cmdlet, as follows:

Get-WinEvent -FilterHashtable @{LogName="Security";ID=4624} | Select-Object -Property *

For the purposes of this post, we will be focusing on the Properties property. Feel free to experiment with the others on your own!

Event Properties

With the advent of EVTX XML formatting, programmatic access to information of interest was simplified. Each logged event has several properties that can be used to customize filtering and output. To exemplify this let’s investigate Windows event 4624 An account was successfully logged on.

Toggling the event viewer to view the XML content of a single instance of Windows event 4624 An account was successfully logged on displays the following:

<Event xmlns="">
    <Provider Name="Microsoft-Windows-Security-Auditing"
Guid="{54849625-5478-4994-a5ba-3e3b0328c30d}" />
    <TimeCreated SystemTime="2020-08-30T16:42:01.4527164Z" />
ActivityID="{6711307d-75d3-0004-e630-1167d375d601}" />

    <Execution ProcessID="868" ThreadID="1100" />
    <Security />
    <Data Name="SubjectUserSid">S-1-5-18</Data>
    <Data Name="SubjectUserName">System1$</Data>
    <Data Name="SubjectDomainName">WORKGROUP</Data>
    <Data Name="SubjectLogonId">0x3e7</Data>
    <Data Name="TargetUserSid">S-1-5-18</Data>
    <Data Name="TargetUserName">SYSTEM</Data>
    <Data Name="TargetDomainName">NT AUTHORITY</Data>
    <Data Name="TargetLogonId">0x3e7</Data>
    <Data Name="LogonType">5</Data>
    <Data Name="LogonProcessName">Advapi</Data>
    <Data Name="AuthenticationPackageName">Negotiate</Data>
    <Data Name="WorkstationName">-</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">0x1ac</Data>

<Data Name="ProcessName">C:\Windows\System32\services.exe</Data>
    <Data Name="IpAddress">-</Data>
    <Data Name="IpPort">-</Data>
    <Data Name="ImpersonationLevel">%%1833</Data>
    <Data Name="RestrictedAdminMode">-</Data>
    <Data Name="TargetOutboundUserName">-</Data>
    <Data Name="TargetOutboundDomainName">-</Data>
    <Data Name="VirtualAccount">%%1843</Data>
    <Data Name="TargetLinkedLogonId">0x0</Data>
    <Data Name="ElevatedToken">%%1842</Data>

Viewing and understanding the XML format of event logs is great, but how does it provide programmatic benefit? Enter the capabilities of the Get-WinEvent Cmdlet!

In the previous section we strategically highlighted the Properties property of the Get-WinEvent Cmdlet.

Get-WinEvent -FilterHashtable @{LogName="Security";ID=4624} -MaxEvents 1 | Select-Object -ExpandProperty Properties

Very interesting! The properties from the XML view (Left) match the output of our Cmdlet (Right) (with the exception of some hexadecimal translations (e.g. 0x1ac > 428) and the value that I omitted for security purposes)

You may notice that the output for our PowerShell Cmdlet does not specify the name of the event property, only the value is returned. This limitation requires us to have a basic understanding of the event being filtered. As an additional note, the output is returned in an array.

To select a specific value, you must specify its position. For example, the TargetUserName property of our event is in the 6th position of our XML output. Since PowerShell arrays begin with the first value at position 0, and the value we need returned sits in position 6, we will specify 5 to return the value that we have chosen:

(Get-WinEvent -FilterHashtable @{LogName="Security";ID=4624} -MaxEvents 1).Properties[5]

Logon Event Function

The proof of concept shown above provides great insight into the capabilities and functionality that exists for programmatic access to the event log, but real power lies in the ability to create our own objects based on that functionality. Let’s do just that by creating a function using the LogonType (position 9) and TargetUserName (position 6) event properties. We will also filter the event’s message to determine whether an elevated token was assigned to the logon. Comments and a help menu have been provided within the function:

function Get-LogonEvents {

Whole Function:

function Get-LogonEvents {
#COMMENT:The following creates a help menu for the custom function. The help menu details specifics such as the description and applicable parameters.
    Parses Windows logon events.

Parses Windows logon event ID 4624 and uses event properties to create a custom object. The output of this cmdlet will capture the logon time, user, logon type, elevation status, and computer name.

    .Parameter ComputerName

    The Hostname or IP Address of the remote system to audit.



    Pulls logon events from the local computer's audit log.


    Get-LogonEvents -ComputerName StruxDC01

    Pulls logon events from a remote computer's audit log.


    Get-LogonEvents StruxDC01

    Pulls logon events from a remote computer's audit log.


#COMMENT:The following enables the use of the ComputerName parameter, which allows an auditor to audit remote systems.





        $ComputerName = $env:COMPUTERNAME


#COMMENT:The following use of the Get-WinEvent cmdlet filters the Security Log (LogName=”Security”) for logon events (ID=4624).

    Get-WinEvent -ComputerName $ComputerName -FilterHashtable @{LogName = "Security"; ID = 4624} | ForEach-Object {

#COMMENT:The following extracts the LogonType property, which sits in the 9th position of the properties array, from each instance of Event ID 4624 ($[8]). The logon type is translated from a numeric integer into human readable form using PowerShell's switch functionality. Additionally, the event’s message is queried to determine whether the assigned token is elevated.

        switch ($_) {

            {$[8].value -eq 2} {$logonType = "Interactive"}

            {$[8].value -eq 3} {$logonType = "Network"}

            {$[8].value -eq 4} {$logonType = "Batch"}

            {$[8].value -eq 5} {$logonType = "Service"}

            {$[8].value -eq 7} {$logonType = "Unlock"}

            {$[8].value -eq 8} {$logonType = "NetworkCleartext"}

            {$[8].value -eq 9} {$logonType = "NewCredentials"}

            {$[8].value -eq 10} {$logonType = "RemoteInteractive"}

            {$[8].value -eq 11} {$logonType = "CachedInteractive"}

            {$_.Message -match "Elevated Token:+.+Yes"} {$elevationStatus = $TRUE}

            {$_.Message -match "Elevated Token:+.+No"} {$elevationStatus = $FALSE}

            default {$logonType = "Undetermined"}


#COMMENT:The following creates a custom object to be displayed as the function's output. Properties of the output include the logon time, user, logon type, and computer name. The user property, which sits in the 6th position of the properties array, is extracted from each instance of Event ID 4624 ($[5]).


            LogonTime = $_.TimeCreated

            User = $[5].value

            LogonType = $logonType

            IsElevated = $elevationStatus

            ComputerName = $ComputerName




To request support for custom PowerShell, please reach out to our sales team.



As a cybersecurity firm with deep roots in the Department of Defense (DoD) cybersecurity community, we provide specialized services in the areas of compliance, vulnerability management, cybersecurity strategies, and engineering solutions. Since 2013, we’ve partnered with hundreds of organizations within and outside the DoD to understand and proactively manage their risk. Our strength within the DoD has allowed us to easily translate best practices to our clients in other industries including Energy, Manufacturing, Architecture, Education, and Aerospace.