PowerShell Scripts in a Management Pack Part 2

In a previous blog post, I explained how to run PowerShell scripts from a management pack.  I honestly don’t remember why I titled that “Part 1” because I don’t remember what I figured that Part 2 was going to be.  Turns out that using the native PowerShell modules in OpsMgr R2 is a perfect topic for Part 2 which I’ll present here (just pretend that I meant that all along). 

Prior to R2 we could launch PowerShell scripts from a management pack, but we had to do it by building the command line and launching powershell.exe.  This was more complicated, less efficient, and had the disadvantage that the agent didn’t actually know that it was running a script.  If that script hit an error, we didn’t know about because the agent simply knew that it successfully launched the executable.  Now that we have a native module, this is going to be much more efficient and reliable.  I had some disclaimers in that post about overhead from PowerShell that don’t apply with the new modules.  Keep in mind that PowerShell still has to be installed on the target agent.

There are some different details to know about using these modules though, and that’s exactly the point of this post.  I updated my sample application with PowerShell equivalents of a discovery and a property bag script, so you might want to grab that to have as a reference as you read through the rest of this post.

Scripts

In the last post I mentioned that the scripts don’t really change from their VBScript counterparts.  We still create discovery data and property bags using the MOM.ScriptAPI object – we’ll just need to use the New-Object cmdlet in PowerShell instead of the CreateObject method in VBScript.  That’s really the only difference when we were calling PowerShell.exe with the Command, but there are some other changes we’re going to need when using the native R2 modules. 

Parameters

The first change is how we are going to accept parameters into the script.  Note that the term “parameter” is going to get a little tricky here.  Modules have parameters, and we can pass parameters to a script.  As you’ll see below, the PowerShell modules have a module parameter called Parameter to make things even more confusing.  I’ll do my best to keep the wording clear though, and you shouldn’t have too tough of a time sorting all of this out. 

I’ll show how to pass the values to the script parameters below, but for now let’s just worry about the script itself.  Instead of accessing positional arguments with the $args variable (the equivalent of WScript.Arguments in VBScript) we’re going to use named parameters.  We accept these into the script using the param() command at the start of the script specifying the variables that the script is accepting the parameters into.

For example, for the queue statistics script in my sample application, we need to pass in two parameters - $topFolder and $debug.  We do that with the following line:

 param($topFolder,$debug)

This would allow us to specify named parameters on the command line when executing the script.  This would look something like below for the Queue Statistics script.

 .\QueueStats.ps1 -topFolder 'C:\StoreQueues' –debug $false

 

We might type that command line when testing the script, but we won’t be putting it into the management pack – I’ll show how we do that below.  Just want to give you a basic idea of how PowerShell works with named parameters if you aren’t already familiar with it.

Returning Data

In VBScript, we return data using the Return method on the MOM.ScriptAPI object if we are returning discovery data or a single property bag.  If we are returning multiple property bags, we use ReturnItems().  Those aren’t going to work very well from the R2 PowerShell modules though.  Instead, we can simply return the objects themselves from the script.  In PowerShell, you do this by typing the name of the variable on a line all by itself.

This is best illustrated by the following examples which show the PowerShell equivalent to a VBScript snippet:

Discovery Data  
VBScript PowerShell
 Set oAPI = CreateObject("MOM.ScriptAPI") Set oDiscoveryData = oAPI.CreateDiscoveryData() … … oAPI.Return(oDiscoveryData)
 $api = New-Object -comObject 'MOM.ScriptAPI' $discoveryData = $api.CreateDiscoveryData() … … $discoveryData
   
Single Property Bag  
VBScript PowerShell
 Set oAPI = CreateObject("MOM.ScriptAPI") Set oBag = oAPI.CreatePropertyBag() … … oAPI.Return(oBag)
 $api = New-Object -comObject 'MOM.ScriptAPI' $bag = $api.CreatePropertyBag() … … $bag
   
Multiple Property Bags  
VBScript PowerShell
 Set oAPI = CreateObject("MOM.ScriptAPI") For Each object in collection     Set oBag = oAPI.CreatePropertyBag()     …     … Next oAPI.ReturnItems()
 $api = New-Object -comObject 'MOM.ScriptAPI' foreach (object in collection) {     $bag = $api.CreatePropertyBag()     …     …     $bag }

Note that in the multiple property bag example, we’re simply returning each bag as we create it. That’s not complicated but certainly a different approach than in VBScript where we stack up all of the objects before returning them at once with ReturnItems().  The condition detection or write action that is going to accept the output will work against each property bag returned from the script.  The end result is identical to using ReturnItems() in VBScript – just looks a little different in the script.

Consoles

The PowerShell modules will not be accessible in the Operations Console in R2 (no word yet if they will be in future releases).  You are still going to be limited to writing scripts with VBScript or JScript if you are living only in that console.

You will be able to use the new modules in the Authoring Console, although wizards aren’t going to be available for them.  You aren’t going to be able to simply select New Rule and see something like Timed PowerShell Script.  You’re going to have to create custom modules, monitor types, monitors, and rules if you want to use the PowerShell modules.  For creating workflows based on scripts this is typically what you want to do anyway to be able to use your script in multiple workflows and to present a nice set of overrideable parameters to the operator.  Really shouldn’t be much more complicated than other custom modules that you’ve built.

Modules

Discoveries

There is only a single data source module for executing PowerShell scripts -  Microsoft.Windows.TimedPowerShell.DiscoveryProvider.  This is the functional equivalent of the timed script discovery provider that you’ve probably used for creating a discovery with VBScript.  I’m not going to list out all the parameters for the module since you’ll be familiar with most of them – IntervalSeconds, ScriptName, ScriptBody, etc.  The new ones are going to be SnapIns and Parameters which I’ll get to below.

No real trick to using the module other than defining the new parameters.  You’ll create a new discovery with that data source, plug in your script, and you’re on your way.

Property Bags

Scripts returning property bags are used for most monitors and rules using scripts.  These are going to be trickier than the discovery scripts using R2 because we don’t have a data source module.  We do have a probe action though called Microsoft.Windows.PowerShellPropertyBagProbe, so it’s a matter of creating a data source module using it and a System.Scheduler. 

In the sample application, I have three data source modules for PowerShell property bag scripts as follows:

  • Demo.StoreApp.TimedPowerShell.PropertyBagProvider
  • Demo.StoreApp.TimedPowerShell.EventProvider
  • Demo.StoreApp.TimedPowerShell.PerformanceProvider

I’m not going to go into the details of each – you can have a look at the them yourself.  They are the functional equivalents of the TimedScript modules in the Windows Library management pack.  The first returns a property bag while the other two use the first one and then map the property bag output to either an event or perf data.

New Parameters

Parameters Parameter

Yes I know, a parameter called Parameters – sounds a little tricky.  Actually pretty straightforward though.  We have a module parameter that defines the script parameters that are going to be passed to our PowerShell script.  This replaces the Arguments parameter on the VBScript modules and provides a more structured definition of the arguments being passed to the script.

The other thing that’s going to make this a bit tricky is that we’re going to have to directly edit the XML in order to provide the parameters.  The structure is pretty simple, but you may not be familiar with launching an external editor from the Authoring Console.  I’m not going to go into the details of that here, but I may do another blog post on it.  It may seem a bit confusing how to do this and also why you have to. 

The structure is pretty simple as you can see from the example below – just a matter of providing a name and value for each parameter.  This is for the queue discovery in my sample application.

 <Parameters> 
  <Parameter> 
    <Name>sourceID</Name> 
    <Value>$MPElement$</Value> 
  </Parameter> 
  <Parameter> 
    <Name>managedEntityID</Name> 
    <Value>$Target/Id$</Value> 
  </Parameter> 
  <Parameter> 
    <Name>topFolder</Name> 
    <Value>$Target/Property[Type="Demo.StoreApp.ComputerRole.CentralServer"]/QueuePath$</Value> 
  </Parameter> 
  <Parameter> 
    <Name>computerName</Name> 
    <Value>$Target/Host/Property[Type="Windows!Microsoft.Windows.Computer"]/PrincipalName$</Value> 
  </Parameter> 
  <Parameter> 
    <Name>debug</Name> 
    <Value>$true</Value> 
  </Parameter> 
</Parameters> 

The first line of the script this module would be calling is the following Param command which collects each of the named parameters.

param($sourceId,$managedEntityId,$topFolder,$computerName,$debug)

Snap Ins Parameter

The other parameter is to define snap ins that your script requires.  Your script may not need any in which case you won’t use this parameter.  I don’t have in my sample application since those scripts just use the file system.  To show an example of the parameter though, consider a script that requires access to Operations Manager data – something you might run from a Management Server.  That would require the client snap-in which is called Microsoft.EnterpriseManagement.OperationsManager.Client.  To specify this snap-in for your script, you would use the following

 <SnapIns>
  <SnapIn>Microsoft.EnterpriseManagement.OperationsManager.Client</SnapIn>
</SnapIns>

Samples

Rather than paste a bunch of XML in here, you can just grab the latest version of the sample application attached to the post.  I have a discovery script and property bag script in there that have plenty of comments.  I haven’t updated the document yet, but I’ll get to that.  I didn’t want to hold off on the post.

Demo.StoreApp.xml