Our Blog

Working with Windows Events with PowerShell

As you may (and should) know Event Log is your first place to look for explanations on why server/client is behaving the way it is behaving. You simply start the Event Viewer and you browse thru different logs. Be it  Windows, Exchange or other logs you will find yourself in a situation there are thousands of logs logged each minute on busy servers. In crisis situation you would open up Event Viewer

Find what you were looking for and close your case. But it's not always that easy… there are times where you have to query multiple servers or run your checks daily looking for particular events happening on your infrastructure.

How do I work with Event Logs with PowerShell

Microsoft brings you Get-WinEvent as a standard to work with Windows Event Log since some time now. There are other methods but Microsoft really wants you to use this one as it supports all the bells and whistles provided by Microsoft. And for the most part it does it's just pretty good. You can query things just the way you want. But let's work on the example. Lets assume we want to check when the last few patches were installed on AD1 machine, and that you want to get it from Event Logs.

# Find out the structure of event
Clear-Host
Get-WinEvent -FilterHashtable @{ LogName = 'Setup'; Id = 2 } -ComputerName 'AD1' | Format-List *

You had to write simple query for Setup Event Log and look for Event ID number 2. Finally using Format-List * gives us all information about the events.

In example above we  first checked all data for the Event, and then subsequently verified the information we need, and just queried for exact results we needed. While the results provide us a nice way to get what we needed…. there is one problem. The Message field contains a lot of additional information we don't need. What if we're interested in particular Windows Update and not all updates installed?

We would now need to get the Message, parse it, split it in multiple chunks and output information. This means a lot of additional work that is subject to break. Of course you could always use Like and search for particular information just like below.

Get-WinEvent -FilterHashtable @{ LogName = 'Setup'; Id = 2 } -ComputerName 'AD1' -MaxEvents 10 | Where-Object { $_.Message -like '*KB4103723*' } |  Format-List Message, TimeCreated, MachineName

But this is a simple Event. There are far complicated events that may not be easy to parse like that. What if there is a simpler way?

PSEventViewer for the rescue

Lets take a look what PSEventViewer can do on the very same Event Log query as above.

Import-Module PSEventViewer
Get-Events -LogName 'Setup' -ID 2 -ComputerName 'AD1' -MaxEvents 1 | Format-List *
Get-Events -LogName 'Setup' -ID 2 -ComputerName 'AD1' -MaxEvents 10 | Format-List MachineName, IntendedPackageState, PackageIdentifier, Date, ErrorCode

Noticed something different? How all the relevant information been provided to you on a silver platter?

Much easier ha? Now the query for particular KB could be even simpler…

Get-Events -LogName 'Setup' -ID 2 -ComputerName 'AD1' -MaxEvents 10 | Where { $_.PackageIdentifier -eq 'KB4103723' } | Format-List MachineName, IntendedPackageState, PackageIdentifier, Date, ErrorCode
PSEventViewer Overview

PSEventViewer (Get-Events) is really useful PowerShell wrapper around Get-WinEvent. Provided above example just shows one of the features you may be interested in (simple way of getting “hidden” events data). The other 2 important features are:

  • Parallel processing of multiple servers

First feature is an important one. While Get-WinEvent can query results in fast and efficient way things get complicated when you want to query 2, 5, 10 servers. You may think it's really not worth the effort to introduce parallel processing into reading Event Logs but I assure you it makes all the difference. In my special module that queries all AD servers for certain Events it was able to bring down the time from 50 minutes to under 18 minutes. For my other Client which has few more AD servers and a bit different architecture with heavier load the script changed the time to query all AD Servers from 15 hours to just around 3 hours. That's a huge difference. Of course it wasn't the only change I did but it was crucial one.

  • Splitting event ID's into multiple chunks and querying the data

The second thing I did for the other script was to query event logs just once for multiple ID's instead of querying multiple times for 1-5 ID's at the time. I've noticed that whether you ask for 5 ID's or 20 of them the time difference is minimal. So if your initial query took 1 minute to execute to get 5 Event ID's on 5 AD Servers and you wanted to query it 4 times for different sets of ID's it would take 1 minute * 5 servers * 4 times. It would take 20 minutes to query 5 servers, 4 times. With new approach you make query once, you parallel all servers at same time and just wait for output to come in more or less 2 minutes (granted there can be other things in play here..). You may say here…

But wait, you could always query Get-WinEvent for multiple events!

That is true! Until you reach 23 events…

While the error message says:

No events were found that match the specified selection criteria.

That's far from truth. You will get same message whether you query 2 MB Security Log and same when you query 50GB Security Log. You will even get similar response in Event Viewer so it does seem like a limitation of Windows.

With Get-Events you get slightly different story. For the purpose of demonstration what happens I've used the Verbose parameter. 

As you can see the story is quite different. Verbose message shows us there were 32 Event ID's to query and that the function split the events in 2 chunks and actually run them in parallel. One finished quickly because the event log didn't contain much events to process in regards to those ID's. The other one took over 1 minute and returned 2780 events. Keep in mind those 2780 events are processed and provided with those hidden fields. You can't compare it with clean Get-WinEvent which would return a bit quicker as it doesn't do the post processing that Get-Events does. You get few more kinks thou.

Installing module from PowerShellGallery

While you can use the script in a standard way by downloading it from GitHub, putting it in right places and getting it to run…there is much simpler way. Since the script was published to PowerShell Gallery you can simply install the module and run it from anywhere. Just use Install-Module PSEventViewer.

Install-Module -Name PSEventViewer 

If you ever need to update it…

Update-Module -Name PSEventViewer 

So what are you waiting for ? Go grab it!

Code is published on GitHub
Issues should be reported on GitHub
Code is published as a module on PowerShellGallery
Module has it's dedicated page parked here
Tags: , , , , ,

This is a unique website which will require a more modern browser to work! Please upgrade today!