PowerShell

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

This post was last modified on 10 czerwca, 2018 13:23

Przemyslaw Klys

System Architect with over 14 years of experience in the IT field. Skilled, among others, in Active Directory, Microsoft Exchange and Office 365. Profoundly interested in PowerShell. Software geek.

Share
Published by
Przemyslaw Klys

Recent Posts

Active Directory Replication Summary to your Email or Microsoft Teams

Active Directory replication is a critical process that ensures the consistent and up-to-date state of…

2 tygodnie ago

Syncing Global Address List (GAL) to personal contacts and between Office 365 tenants with PowerShell

Hey there! Today, I wanted to introduce you to one of the small but excellent…

5 miesięcy ago

Active Directory Health Check using Microsoft Entra Connect Health Service

Active Directory (AD) is crucial in managing identities and resources within an organization. Ensuring its…

7 miesięcy ago

Seamless HTML Report Creation: Harness the Power of Markdown with PSWriteHTML PowerShell Module

In today's digital age, the ability to create compelling and informative HTML reports and documents…

8 miesięcy ago

How to Efficiently Remove Comments from Your PowerShell Script

As part of my daily development, I create lots of code that I subsequently comment…

9 miesięcy ago

Unlocking PowerShell Magic: Different Approach to Creating ‘Empty’ PSCustomObjects

Today I saw an article from Christian Ritter, "PowerShell: Creating an "empty" PSCustomObject" on X…

9 miesięcy ago