This is a place for Systems Administrators and IT Professionals to find and share ideas, solutions and templates. If you have something that helps you solve a problem, chances are it will help someone else too. So pay it forward and send an email to TheAgreeableCow at gmail. Full mudos to you!

Friday 1 June 2012

Using Get-WinEvent and XML filters to query Event Viewer

In previous roles in IT support and network administration I quickly leaned the benefit of Windows Event Viewer. Fundamentally for me it served two purposes; a retrospective log where I would go and seek an answer to something that had just happened, or alternatively a proactive log where I could get a heads up on potential issues.

Proactive is always good, right? The problem is there is only so much time in the day to spend scrolling through all of that noise. There needs to be a process of filtering and reporting, which allows you to get to important information efficiently. Now, there are a number of commercial programs around that manage this already, so if your scale and budget are so inclined it's a very valid solution. Event Subscriptions are also another method of collecting logs from dispersed machines. Combined them with tasks and you've got a nice little system for gathering and alerting.

In this post however, I'm going to look at gathering event logs using powershell with the get-winevent cmdlet. More importantly, we're going to look at including an XML filter in your query to make the whole process much, much faster. Finally, I've added some code to export the logs to a CSV file or an email for reporting purposes.

The example below is a mini project to collect print server job logs as it serves as a good example of the scenario above. By default the individual user job logs are not enabled. So the first thing we need to do is turn on Print Services Operational logging via Event Viewer > Applications and Service Logs > Microsoft > Windows > Print Service > Operational > right click to Enable Log.

Hands up who loves the way you can doing something in a GUI and it produces the code for you in the background? Well, Windows Event Viewer has a neat way of doing GUI based filtering, which result in XML based queries that you can use in your coding.

Open up event Viewer and create a basic filter on an EventID through Actions > Filter Current Log. Here's an example using EventID 307 (which is a successful print job).

If you then click on the XML tab at the top, you will see the XML query string that is being used. This is what we need to copy into the get-winevent cmdlet.

In your script, the string needs to simply be defined as a variable and enclosed with an apostrophe, for example:

The second part of this process is working out what parameters are contained in the event, so you can define them as variables for manipulation in your script. So, going back to one of the events in the log simply click on the Details tab. Here you will see all of the parameters that can be retrieved in a Friendly View.

I've masked out some details in the above example,but you can easily see how it is laid out. The syntax for your script is also straight forward. Firstly a single line for the xml conversion, then simply call the parameters working through the levels of the xml tree. In this case User Data > DocumentPrinted > Parameter. For example:

So, here is the complete code for this project, including writing the output to a CSV file and attaching it (or including it as the body), to an email

         (oo)  ok
   /------\/  /
  / |    ||
 *  /\---/\
    ^^   ^^


  1. Hi,

    I have used your script, but we had a slight issue in that you declare param8 as number of pages printed, however in your screenshot param8 does not exist as its called param7 as well.

    To get round this, we modified your code that takes the page number and puts it into a separate column and removes it from the print size

    #Get print job details
    $time = $LogEntry.TimeCreated
    $entry = [xml]$LogEntry.ToXml()
    $docName = $entry.Event.UserData.DocumentPrinted.Param2
    $Username = $entry.Event.UserData.DocumentPrinted.Param3
    $Computer = $entry.Event.UserData.DocumentPrinted.Param4
    $PrinterName = $entry.Event.UserData.DocumentPrinted.Param5
    $PrintSize = $entry.Event.UserData.DocumentPrinted.Param7
    $PrintPages = $entry.Event.UserData.DocumentPrinted.Param8

    #Putting a varible into " " will turn it into a text field

    $Pages = "$PrintSize"
    $TruePrintSize = $Pages.Substring(0, $Pages.IndexOf(" "))

    $TruePages = $Pages.Substring($Pages.IndexOf(" "),$Pages.Length – $Pages.IndexOf(" "))

    We then replaced +$PrintSize+ "," +$PrintPages+ with our new varibles +$TruePrintSize+ "," +$TruePages+

    Thanks for the original code though

  2. Awesome article! Made my script, based on yours, to monitor RD Gateway users. THX!

  3. Wow, cool post. I'd like to write like this too - taking time and real hard work to make a great article... but I put things off too much and never seem to get started. Thanks though. event directory