The Wiert Corner – irregular stream of stuff

Jeroen W. Pluimers on .NET, C#, Delphi, databases, and personal interests

  • My badges

  • Twitter Updates

  • My Flickr Stream

  • Pages

  • All categories

  • Email Subscription

    Enter your email address to subscribe to this blog and receive notifications of new posts by email.

    Join 716 other subscribers

Windows event log querying from the command line: wevtutil (with XPath query parameters and XML output)

Posted by jpluimers on 2025/04/23

A while ago, I needed to investigate reboot events on some Windows 10 systems. I wanted to use the console instead of the eventvwr GUI Event Viewer.

There is a tool for that called wevtutil which – like eventvwr – uses XPath query parameters and produces XML output.

Postprocessing XML can be a thing, but since .NET has great XML support, you can use PowerShell for that (which for me often is way easier than going the XSLT route, for instance because Windows lacks built-in console XSLT tooling).

Based on the help and the below links, my query command then on these machines turned out to be this:

wevtutil query-events System /reversedirection:true "/query:*[System[EventID=6005 or EventID=6006]]" > reboot-events.xml

I expanded all options to their full form except reverseddirection, as despite the built-in help – see [Wayback/Archive] wevtuti built-in help: “wevtutil /?” and “wevtutil query-events /?” – it is not supported (bug alert!):

Invalid option reverseddirection. Option is not supported.
The parameter is incorrect.

Some links for both my link archive and if you are interested to play around with this:

  • [Wayback/Archive] How to find out the last time Windows rebooted and started up
    • Event ID 6006: “The event log service was stopped.” This is synonymous to system shutdown.
    • Event ID 6005: “The event log service was started.” This is synonymous to system startup.
  • [Wayback/Archive] How to query logs in Event Viewer using the command line (which explains about wevtutil which is available as of Windows Vista)
  • [Wayback/Archive] wevtutil | Microsoft Learn

    Enables you to retrieve information about event logs and publishers. You can also use this command to install and uninstall event manifests, to run queries, and to export, archive, and clear logs.

  • [Wayback/Archive] windows – Wevtutil\Event Viewer: Getting list of events with different event ids using XPath Filter – Stack Overflow (thanks [Wayback/Archive] Anthony J. and [Wayback/Archive] Eryk Sun):

    Q

    I need to get a list of events that have id of 6005 or 6006 using “wevtutil” tool. This command works fine:
    wevtutil qe system /rd:true /q:*[System[EventID=6005]]
    
    But I need to get both events with ID 6005 and 6006. I tried
    wevtutil qe system /rd:true /q:*[System[EventID=6005 or EventID=6006]]
    
    But it returns
    Too many arguments are specified. The parameter is incorrect.
    How should I fix it?

    C

    wevtutil.exe uses the C runtime’s wmain entry point, which tokenizes the command line according to the rules documented in [Wayback/Archive] Parsing C++ Command-Line Arguments. So you need double quotes to prevent the query from getting split as separate arguments, e.g. "/q:*[System[EventID=6005 or EventID=6006]]" or even odd-looking /q:*[System[EventID=6005" or "EventID=6006]].

  • [Wayback/Archive] `main` function and command-line arguments (C++) | Microsoft Learn
  • [Wayback/Archive] windows server 2008 – Parse wevtutil XML into a database? – Server Fault (thanks [Wayback/Archive] Skyhawk and [Wayback/Archive] sysadmin1138):

    Q

    I’m by no means an XML expert. Ergo, I’m having trouble importing Windows event XML from wevtutil into an SQL database.

    …

    (note: this would be a recurring process, so it can’t require GUI-only tools.)

    A

    I’m doing exactly this in a script by way of PowerShell.

    …

    The command to get the event data is what you already know.
    wevtutil qe Security /r:$DC /q:"*[System[((EventID=$LogonID or EventID=$FLogonID or EventID=$LogoffID or EventID=$LockoutID) and TimeCreated[@SystemTime > '$LUFilterString'] and TimeCreated[@SystemTime < '$NowFilterString'] )]] " > $DC-events.xml
    The variables in that should be clear. I’m tracking login, logout, and lockout events. Generating the “NowFilterString” in the funny format wevtutil requires:
    $Now=get-date
    $Msec=$now.Millisecond
    $NowFilterString=$Now.AddSeconds(-1).AddMilliseconds(-$Msec).ToUniversalTime().ToString("O")
    
    I’m truncating the milliseconds down to zero to better handle edge cases.
    So now you have an XML file. Now what? To parse that XML file:
    get-content ".\$DC-events.xml" | foreach {
        $Event=[xml]$_
        $DateTime=[datetime]$Event.event.System.TimeCreated.GetAttribute("SystemTime")
        codecodecodecode
    }
    
    Accessing individual elements is done by:
    foreach ($Data in $Event.event.EventData.get_childNodes()) {
                if ($Data.Name -eq "TargetUserName") { $User=$Data."#text"}
                elseif ($Data.Name -eq "IpAddress") {$IP=$Data."#text"}
            }
    
    Or another example
    foreach ($Data in $Event.event.EventData.get_childNodes()) {
        if ($Data.Name -eq "TargetUserName") {$User=$Data."#text"}
           elseif ($Data.Name -eq "WorkstationName") {$MachineName=$Data."#text"}
           elseif ($Data.Name -eq "IpAddress") {$IP=$Data."#text"}
           # Ensure only failed logins to the right domain are processed:
           elseif ($Data.Name -eq "TargetDomainName") {$Domain=$Data."#text"}
        }
    
    I hope this helps you figure out XML parsing. Since this is PowerShell, most of these are readily convertible to standard .NET calls.

In the search queries used, you can see that a some moments I was not exactly sure what to search for, but Google luckily gave relevant results nonetheless:

--jeroen


https://gist.github.com/jpluimers/3a198160e4aa0ab3596ccf3ec9dcc03a

Leave a Comment