Using SDK criteria objects in PowerShell for optimized SCOM data queries

Querying data (Agents, MonitoringObjects, Events, Alerts etc.) in SCOM with PowerShell is a piece of cake. Simply use get-scomalert, get-scomevent or some other suitable Cmdlet and you are done.

 

Good (efficient) vs. bad (inefficient) data queries

But creating performance and resource optimized queries can be quite challenging! What do I mean with that?
Let’s have a look at a common example:
I want to get all Alerts in the Management Group matching the ResolutionState = 10.
The bad way to do that is:

 PS> get-scomalert | ? {$_.ResolutionState -eq 10}

Why is that bad? Because you will download ALL alerts from the Management Group to your local shell and only THEN filter the data with where-object on your local shell. There is nothing special to that. In every PowerShell workshop you learn, that filtering on the source Cmdlet is always the best way to reduce the data volume and thus optimizing the query.
So the best way to accomplish the task in our case is to take advantage of the “-criteria” parameter of the get-scomalert Cmdlet:

 PS> get-scomalert -criteria 'ResolutionState = 10'

You can find detailed information about the Criteria syntax on MSDN here .

 

What about querying for other data like monitoring objects or events?

Recently I had to write a script, which should retrieve certain collected SCOM events (based on ID and TimeAdded) and take some action on a specific monitoring object (based on a class and the DisplayName property). Because the customer environment was quite huge I wanted to query the SCOM database for events and monitoring objects the most efficient and optimized was possible.
I first checked the help for get-scomclassinstance  and get-scomevent .
Both Cmdlets does not provide a “-criteria” parameter,meaning a way to efficiently filter data on the server side!
So by using these Cmdlets I had to write my queries like this:

 PS> get-scomevent -ID 1234 | ? {$_.TimeAdded -gt (get-date).AddHours(-1).ToUniversalTime()}

or

 PS>$class = get-scomclass -name “Microsoft.Windows.Computer”
PS>get-scomclassinstance -class $class | ? {$_.DisplayName -eq “my.computer.tld”}

Both queries are quite inefficient and resource intensive when you have thousands of matching events or thousands of Windows Computer objects in your environment.

 

The efficient way: Using Criteria objects in SDK methods

There is a much more efficient way to handle this:
The .NET type Microsoft.EnterpriseManagement.EnterpriseManagementGroup provides a bunch of useful methods to efficiently query the SCOM database:

2016-04-06-QueryMethods
Among other e.g. the methods GetMonitoringObjects() and GetMonitoringEvents() . Both methods are described on MSDN here and here.
Problem is, that I always struggle with the MSDN description and examples as they are mostly written for hard core Devs and not an IT Pro wuss like me…
Let’s have a look at the overload list of the GetMonitoringObjects() method:
2016-04-06-MethodOverload
You see, that this method accepts a criteria string! Therefore filtering on the source should be possible, even without using the Cmdlets.

Example 1: Querying for monitoring objects with GetMonitoringObjects()

I will show you how to use this method with a combination of class (Microsof.Windows.Computer) and a criteria (DisplayName = ‘a.b.c’):
We need some prerequisites, so let’s start with that:

Step 1: Create our ManagementGroup object:

  PS> $MG = get-scommanagementgroup

Step 2: Create our class object

  PS> $WindowsComputerClass = Get-scomClass -Name "Microsoft.Windows.Computer"

Step 3: Build the criteria string

  PS> $strCriteria = "DisplayName = 'a.b.c'"

Now comes the tricky part:
According to the MSDN we need a MonitoringObjectCriteria object!
How do we get such an object? The examples in MSDN are not really PowerShell ready or friendly, but give you a clue on what you have to do there. And we can check the class definition itself.

Step 4: Create a MonitoringObjectCriteria object

 PS> $objMonitoringObjectCriteria = New-Object Microsoft.EnterpriseManagement.Monitoring.MonitoringObjectCriteria($strCriteria,$objWindowsComputerClass)

Now we can finally use this object to retrieve our result:

Step 5: Retrieve the result by combining criteria object and method

 PS> $MonitoringObject = $ManagementGroup.getmonitoringobjects($objMonitoringObjectCriteria)

Example 2: Querying for event objects with GetMonitoringEvents()

I will show you how to use this method with a combination of a collection rule (My.Event.Collection.Rule) and a criteria (TimeAdded > = ‘last hour’).  Like in the first example we need some prerequisites:

Step 1: Get ManagementGroup object

 PS> $MG = get-scommanagementgroup

Step 2: Get a rule object

 PS> $Rule = get-scomrule -name “My.Event.Collection.Rule”

Step 3: Get a DateTime object in UTC for the time one hour before

 PS> $dtmSearchTimeUTC = ((Get-Date).addhours(-1)).touniversaltime()

Ok, now comes the tricky part again: According to the MSDN we need a MonitoringEventCriteria object!
Looking at the MSDN documentation for this class, we can see:

Step 4: Create a criteria string

 PS> $strEventCriteria = "MonitoringRuleId = '{0}' AND TimeAdded  >= '{1:MM/dd/yyyy HH:mm:ss}'" -f $Rule.ID.guid.tostring(),$dtmSearchTimeUTC

Step 5: Now we can create our criteria object

 PS> $objEventCriteria = New-Object microsoft.EnterpriseManagement.Monitoring.MonitoringEventCriteria($strEventCriteria)

And finally we can retrieve our events:

 PS> $colEvents = @($MG.GetMonitoringEvents($objEventCriteria))

Summary

This post described how you can efficiently query the SCOM database and retrieve data types even if the PowerShell Cmdlets do not provide us some kind of filter parameter on the server side itself.
By using methods of the .NET Microsoft.EnterpriseManagement.EnterpriseManagementGroup type you can create complex and powerful data queries and execute them in the most efficient way.

Happy querying!