Welcome to TechNet Blogs Sign in | Join | Help

I did a post bacin February 2008 on running a PowerShell script from a management pack.  This was prior to the native PowerShell modules that came with Operations Manager R2 and that I wrote about on a separate post.  The original information is still valid though since it is the method you need to use if you want to run PowerShell scripts in an SP1 environment.  Until Operations Manager 2007 SP1 is no longer supported, there will be reasons to keep using this method.

I just had some good feedback sent to me identifying some issues with that original post.  Since I understand that people still refer to that information, I went ahead and modified it to include those corrections.

Specifically, the following changes have been made:

  • Changed the command line with the following:
    • Set the execution policy
    • Add the Arguments parameter
    • Fixed some syntax. 
    • Fixed the incorrect statement that CDATA cannot be used for the command line.
  • Uses the parameter for IntervalSeconds rather than a fixed value.
  • Changes the RequiresOutput element to True.

Now that I’ve changed positions to own knowledge for management pack authoring,  a blog more specific that topic was in order. I’ll be moving activities starting today to http://blogs.technet.com/MPAuthor.  Please head over there for announcements on upcoming updates to the MP Authoring Guide and other information regarding MP Authoring.

Not sure if I’ll be updating this blog or not.  Any MP authoring information will be going on the new blog.  If I have any other topics to talk about, then they’ll go in here.  I’ll certainly keep this one intact though since I have several posts that still have some relevant information.

MPAuthor Blog

After 13 years of consulting, I joined the UA team in Redmond last August as a technical writer focusing on management pack authoring.  My initial focus is a new version of the Management Pack Authoring Guide which will provide background concepts, processes, and specific walkthrough examples on creating management packs for Operations Manager 2007.

The first section of the new Authoring Guide went live last Friday.  This is the section on Service Model which describes classes, relationships, and discoveries.  We have already made significant progress on the Composition section which will be the next to be published.  This will give details on custom modules and workflows and hopefully make everyone confident to select menu items with the word Custom in the Authoring Console.  This will be followed by sections on Health Model and Presentation (which will primarily talk about Views).  We are also going to get some good reference information in there to help with forming variables and dealing with different data types.

It’s not common to publish section by section like this.  We would typically complete the entire guide before going live.  It didn’t seem to make any sense to hold back content that people could use immediately though while other sections were being written.  That might have been our only solution back in the days of bound paper manuals, but it certainly doesn’t make any sense in today’s world.

We’re trying to select the order of publication in terms of demand.  The most common questions that I get are related to the Service Model – what classes to create for an application, what base classes to use, the difference between a base class and a hosting relationship, when to use a containment relationship, etc, etc.  That in addition to the fact that building a model is the first step in building a management pack made it pretty obvious that we needed to get that section published first.

Please share any feedback you have on the guide – whether it be positive, negative, corrections, suggestions, etc.  If any of the existing content isn’t clear I want to make sure that we make appropriate modifications.  You provide comments on TechNet, on this blog, or by mailing MOM Documentation Feedback.

I realize I haven’t posted anything in awhile – mainly due to my recent change of job within Microsoft (more information on that later).  I had to get off my lazy butt to write this post though because the MP Author Resource Kit was just released today.  I’ve had the opportunity to play with a beta of these tools, and they are absolutely outstanding.  Some of the tools might be classified under the “nice to have” category but others absolutely change the entire authoring experience.  Oh the time that these could have save me over the past couple of years…..

The rundown of tools in the new resource kit is as follows:

  • Authoring Console
    Not too much has changed from the R2 version other than the new resource kit tools getting integrated into the menus.
  • MP Best Practices Analyzer
    Scans the management pack to identify where it might deviate from best practices.  Even allows automatic resolution for some issues.  Don’t underestimate the recommendations made by this tool.  These are very current best practices that have been identified from experience and lessons learned since Operations Manager 207 was released. Don’t let an MP out the door until it passes this analysis.
  • MP Spell Checker
    Does exactly what the name implies.  Cool thing is it allows you to maintain an exclusion list with all those application specific terms you deal with.  Nothing like a silly spelling error to make your management pack just look cheesy.  No more.
  • MP Visio Generator
    Way cool.  This creates two Visio diagrams for your service model.  One showing class inheritance – all of the base classes that your classes are inheriting from.  The other shows the relationships between all of your classes.  This is great for documentation of your MP but also nice for analyzing another management pack that you didn’t write.  A nice quick diagram of the service model.
  • MP Diff
    Shows the differences between two different management packs.  Useful for people like me who create all kinds of demo and sample MPs and then forget what the differences are.
  • Cookdown Analyzer
    Lack of cookdown in rules and monitors targeted at classes with several instances on an agent is one of the biggest sources of overhead from a management pack.  This tool will identify classes with the potential for multiple instances and then run an analysis on workflows targeted at them to determine if cookdown may break. 
  • All References Add-in
    Right click on any element in a management pack and get a listing of any other referenced elements.   Great for quick sanity checks.
  • Workflow Analyzer
    Finally, a means to debug a running workflow!  Connect to an agent, select a workflow and watch the output from each module.  This one will be useful to operations people in addition to MP authors.
  • Workflow Simulator
    I saved the best for last.  There simply aren’t enough nice things I can say about this one.  Right click on a rule, monitor, or discovery and select Simulate.  You’ll actually run a copy of that workflow right on your workstation and be able to inspect the output from each module right there your workstation.  No need for a connection to a management group.  You could do this on an airplane!  We can actually create custom workflows and test them with just the Authoring Console before ever installing the MP.  Now this changes the whole process and has the potential to save authors tons of time.

Just to show how cool the Workflow Simulator is, let me show a couple of screenshots.  This is the StoreApp management pack that I’ve used at a couple of conferences for a sample.  This particular monitor runs a script that collects a file count from several folders – simulating queues in the sample application.

All I have to do is right click on the monitor and select Simulate.

image

Then I get a dialog box showing me all of the overrideable parameters in case I want to change any for this simulation.  I also get any target variables that are being passed to the monitor.  When actually running, these are values that we would get from the properties of the target object.  In the simulation though we have no target object, so I get the opportunity to provide my own values.

10-21-2009 11-14-10 PM

 

 

 

When I’m ready, I hit Start Simulation, and the monitor runs - listing each module and it’s output!  Have a look at the screenshot below.  I have a timestamp next to each module as it is loaded.  You can see that the first data source is the scheduler.  Once it fires, the script runs outputting three different property bags – one for each folder that my script finds.  I then filter that to a single data source matching the particular folder I’m interested in, and then we see each of the expressions for the health states of the monitor.  Right next to each module in the Data column, I can see the values in the resulting property bag.  If I want to see the actual XML, it’s in the right pane.

image

 

For anyone who is used to only being able to test workflows by installing them and then waiting for them to deploy to the agent this is huge.  An authoring experience not requiring a connection to a running environment, a quick means of testing workflows, and complete exposure into the execution and resulting data from each module in the workflow.  What more could you possibly ask for?

Bottom line – go get the MP Resource Kit now.  I’ve only been using it for a couple of weeks, and I already can’t imagine trying to write an MP without it.

Attached to this post is a version of my sample application for Operations Manager SP1.  This has the process monitor removed that relies on the modules in R2. 

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.

I’ve had a couple of people tell me that the sample application I used for my MMS presentations won’t load in their environment.  That’s because the management pack requires the OpsMgr R2 beta.  It won’t load into an SP1 management group.  I used a monitor in there from the new process monitoring MP in R2. 

I’m about to update the MP with some PowerShell examples.  When I do, I’ll strip out the process monitoring in the original version and have a second version that can load into SP1 for those people without access to an R2 environment. Should just be a couple of days before I get that done.

I’ve been getting this question quite a bit, so I thought I’d give a quick answer to it.  The question is whether we can use the R2 Authoring Console to create management packs for SP1.  The answer is that you absolutely can – with a minor caveat.  There is nothing different about the structure of a management pack for SP1 or R2, and the Authoring Console just creates management packs in XML.  There are R2 specific modules, and using those will make your MP unusable in SP1, but you could use those with any version of the Authoring Console or even editing XML directly.

But what about that one caveat?  When you create a new management pack in the Authoring Console, it will automatically add references to the library MPs – System.Library, System.Health.Library, Microsoft.Windows.Library, and Microsoft.SystemCenter.Library.  The R2 Authoring Console will create those references to the latest versions of those MPs (6.1.7043.0 as of RC version of R2).  You probably aren’t going to use any specific features of those new versions, but if you leave those references, your management pack won’t load in a management group with pre-R2 libraries. 

To fix this, you should be able to just go to the References tab in Management Pack Properties and change the versions of those references.  The Key Token won’t change since the same certificate was used for sealing the different MP versions.  For reference, the SP1 libraries are version 6.0.6278.0, and the RTM libraries are version 6.0.5000.0.

Bottom line is definitely use the R2 Authoring Console regardless of which version of OpsMgr you’re working with.  Several improvements over the SP1 version including the ability to create elements that we used to have to drop out to XML for.

A bit wiped out after delivering four consecutive sessions on management pack authoring.  I'm impressed with people who made it through the entire five hours.  That was definitely not light content.  Seems like they were well received though. 

The sample management  pack that I showed along with a document describing  the MP and the sample application that it's based on is on the OpsManJam site.  I'm planning on continuously updating this MP as l create other example scenarios.  I thought that would be more interesting than continuing to do random examples, and l can keep them all packaged up in one clean MP.  I'm also getting tired of all the random little XML files l have cluttering up my MP library.

I'm getting mocked quite a bit at the conference about my lack of blog updates.  Now that I've gotten through these presentations as well as some other training l was working on, l should be able to get to that.

I recently help someone out getting a monitor working that uses the System.SnmpQueryProvider module in the System.Snmp.Library management pack to perform a query against an SNMP device.  They were getting back an error message showing some cryptic characters in the community string.  They were just providing simple text like "public" for the CommunityString parameter on the module, so this looked pretty strange.

After some research, it turns out that module wants the community string specified in Base64 format.  Nothing special from the MP perspective – you just need to specify the Base64 equivalent of your community string in the CommunityString parameter rather than the simple text.

If you already have a means of converting a string to Base64, then you should be fine.  If not, here’s bit of PowerShell that will do the conversion for you.

$communityString = 'public'
[System.Convert]::ToBase64String([System.Text.Encoding]::UNICODE.GetBytes($communityString))

If I run that code, I get the following value: cAB1AGIAbABpAGMA.  Pasting that into the CommunityString parameter on the module should get you the results you want.  Obviously, if you have a community name different than "public" you would paste that in and use the resulting converted value.

Well, it looks from my last post that I took about a eight month hiatus from the blog.  Fortunately, I've been doing a bunch of OpsMgr in that time, so I have a bunch of content to get out here.  Just a matter of taking the time to write it up.

The one I'll go with first is programmatically creating groups.  Now creating a single group is pretty easy in the Operations Console, so creating a single one from a command line isn't going to get anyone excited.  What is exciting though is if you have a bunch of groups to create - especially if several of these are subgroups.

I'll leave you to figure out the complexity of providing a list of groups - maybe reading from a text file or something.  That's just straight PowerShell work.  What I will give you though is the code to create a new group.  You should be able to use this base code to do whatever complex and repetitive group creation that you might need.

First of all, there is no cmdlet to create a group.  You're going to have to manually create a Microsoft.EnterpriseManagement.Monitoring.CustomMonitoringObjectGroup object, set its properties, and then save it to the management group using the InsertCustomMonitoringObjectGroup method on the management group.  That part's easy.  It will be when I give you the code in a minute anyway.  The tough part of the exercise is setting the membership formula, and we need to go through that before we get into the code.

Membership Formula

The membership formula is an XML expression defining the logic for what objects should be members of the group.  The easiest way to figure out how to build this formula is to configure a group using the dialog box in the Operations Console and then copy out the resulting XML.  There are a couple of things you need to know about this though,  so let me provide a bit of explanation and a couple of examples.

When you export the management pack, you'll find the membership rule in the discovery for the group.  It might take you a little bit of searching through the different discoveries if you aren't familiar with the XML schema of a management pack, but it shouldn't take too long.  A typical discovery will look something like the XML below.

<Discovery ID="UINameSpace9e378de9416945c4820c4cacfd0475fa.Group.DiscoveryRule" Enabled="true" Target="UINameSpace9e378de9416945c4820c4cacfd0475fa.Group" ConfirmDelivery="false" Remotable="true" Priority="Normal"> 
  <Category>Discovery</Category> 
  <DiscoveryTypes> 
    <DiscoveryRelationship TypeID="SCIG!Microsoft.SystemCenter.InstanceGroupContainsEntities" /> 
  </DiscoveryTypes> 
  <DataSource ID="GroupPopulationDataSource" TypeID="SystemCenter!Microsoft.SystemCenter.GroupPopulator"> 
    <RuleId>$MPElement$</RuleId> 
    <GroupInstanceId>$MPElement[Name="UINameSpace9e378de9416945c4820c4cacfd0475fa.Group"]$</GroupInstanceId> 
    <MembershipRules> 
      <MembershipRule> 
        <MonitoringClass>$MPElement[Name="SCIG!Microsoft.SystemCenter.InstanceGroup"]$</MonitoringClass> 
        <RelationshipClass>$MPElement[Name="SCIG!Microsoft.SystemCenter.InstanceGroupContainsEntities"]$</RelationshipClass> 
        <IncludeList> 
          <MonitoringObjectId>d47ea9a0-a039-d399-bec3-4b305269b57e</MonitoringObjectId> 
        </IncludeList> 
      </MembershipRule> 
    </MembershipRules> 
  </DataSource> 
</Discovery>

The only part you want is the MembershipRules section, and that’s what I’ll describe below.

Membership Formula Examples

First, let's look at a typical dynamic membership.  The following dialog box shows a criteria that you might create.

image

That logic would translate to the following membership rule which could be used as is.  There are no GUIDs here to worry about.

<MembershipRule> 
  <MonitoringClass>$MPElement[Name="Windows!Microsoft.Windows.Computer"]$</MonitoringClass> 
  <RelationshipClass>$MPElement[Name="SCIG!Microsoft.SystemCenter.InstanceGroupContainsEntities"]$</RelationshipClass> 
  <Expression> 
    <And> 
      <Expression> 
        <RegExExpression> 
          <ValueExpression> 
            <Property>$MPElement[Name="Windows!Microsoft.Windows.Computer"]/PrincipalName$</Property> 
          </ValueExpression> 
          <Operator>MatchesWildcard</Operator> 
          <Pattern>lgb*</Pattern> 
        </RegExExpression> 
      </Expression> 
      <Expression> 
        <RegExExpression> 
          <ValueExpression> 
            <Property>$MPElement[Name="Windows!Microsoft.Windows.Computer"]/IPAddress$</Property> 
          </ValueExpression> 
          <Operator>MatchesWildcard</Operator> 
          <Pattern>10.1.*</Pattern> 
        </RegExExpression> 
      </Expression> 
    </And> 
  </Expression> 
</MembershipRule>

 

How about a group with explicit members?  Now this is probably a bad example, because you probably won't pragmatically be creating groups with explicit members, but it is possible.

 

image

This generates the following membership rule which includes GUIDs.  These GUIDs really can't be swapped out though since they refer to specific objects.  If you were to programmatically  create something like this, you would presumably use Get-MonitoringObject in Command Shell to get the GUID of the object you're interested in and then add it to the XML.

<MembershipRule> 
  <MonitoringClass>$MPElement[Name="SystemCenter!Microsoft.SystemCenter.ManagedComputer"]$</MonitoringClass> 
  <RelationshipClass>$MPElement[Name="SCIG!Microsoft.SystemCenter.InstanceGroupContainsEntities"]$</RelationshipClass> 
  <IncludeList> 
    <MonitoringObjectId>b3e9befe-173b-78a2-3e41-e19ecb58d9d5</MonitoringObjectId> 
    <MonitoringObjectId>0852e1eb-f854-eb88-2f7d-a1bb0794d18f</MonitoringObjectId> 
    <MonitoringObjectId>6e813ffe-2267-a191-ccbb-566d4ddf95bc</MonitoringObjectId> 
    <MonitoringObjectId>98e3ee12-54c0-f14c-506c-4113bb8f1dcb</MonitoringObjectId> 
  </IncludeList> 
</MembershipRule>

 

Subgroups are something you very well might want to use.  One use of programmatically creating groups is to create a complex group structure which may contain several levels of different groups.

image

 

This would generate the following XML, and in this case we would want to swap out that GUID which refers to the subgroup (Sample Group - Level 2 in the example).

<MembershipRule> 
  <MonitoringClass>$MPElement[Name="SCIG!Microsoft.SystemCenter.InstanceGroup"]$</MonitoringClass> 
  <RelationshipClass>$MPElement[Name="SCIG!Microsoft.SystemCenter.InstanceGroupContainsEntities"]$</RelationshipClass> 
  <IncludeList> 
    <MonitoringObjectId>e499513e-4e67-142e-f1f6-53b24b5195bb</MonitoringObjectId> 
  </IncludeList> 
</MembershipRule>

This would work better with the following.  The end result is the same since we'll still end up with the GUID of the subgroup.  but this example could be installed in other management groups and would make a lot more sense when working with code.

<MembershipRule> 
  <MonitoringClass>$MPElement[Name="SCIG!Microsoft.SystemCenter.InstanceGroup"]$</MonitoringClass> 
  <RelationshipClass>$MPElement[Name="SCIG!Microsoft.SystemCenter.InstanceGroupContainsEntities"]$</RelationshipClass> 
  <IncludeList> 
    <MonitoringObjectId>$MPElement[Name='bwren.GroupDemo.SampleGroupLevel2']$</MonitoringObjectId> 
  </IncludeList> 
</MembershipRule>

 

Finally, we need to talk about empty groups since this is very common situation when programmatically creating groups.  You may automate the creation of a large group structure relying on users to add objects to these groups through the Operations Console once the management pack is loaded.  Empty groups have a very specific structure which is below.

    <MembershipRule Comment="EMPTY_RULE_8eadaced-59c8-4ebc-a4e4-b8428a374442"> 
      <MonitoringClass>$MPElement[Name="SC!Microsoft.SystemCenter.AllComputersGroup"]$</MonitoringClass> 
      <RelationshipClass>$MPElement[Name="SCIG!Microsoft.SystemCenter.InstanceGroupContainsEntities"]$</RelationshipClass> 
      <Expression> 
        <SimpleExpression> 
          <ValueExpression> 
            <Value>True</Value> 
          </ValueExpression> 
          <Operator>Equal</Operator> 
          <ValueExpression> 
            <Value>False</Value> 
          </ValueExpression> 
        </SimpleExpression> 
      </Expression> 
    </MembershipRule> 

As you can see, the basic logic is to check if True equals False.  Since this will obviously never be a true statement, we have no objects that match the criteria.  The other thing you need though is the comment with that specific string on the MembershipRule tab.  There’s no real value in going into the background on that.  Just know that you need it.  If you don’t include it, the group will get created, but you’ll get a pretty ugly error when you try to modify the membership of that group from the Operations Console.

Management Pack Aliases

If you aren’t familiar with management pack aliases, they are separated from management object names with a ! in all the examples above.  An alias is required if you are referring to an object in another management pack.  The alias to that management pack must be in the References section of the management pack containing your group.  In the examples above, you can see the alias SCIG which refers to the System Center Instances Group management pack.  As such, the Reference section would need to contain the following entry.

<Reference Alias="SCIG"> 
  <ID>Microsoft.SystemCenter.InstanceGroup.Library</ID> 
  <Version>6.0.6278.0</Version> 
  <PublicKeyToken>31bf3856ad364e35</PublicKeyToken> 
</Reference>

Your management pack might use a different alias for Microsoft.SystemCenter.InstanceGroup.Library.  I just created a new management pack with the Operations Console, and it used MicrosoftSystemCenterInstanceGroupLibrary6164070. Obviously, you will need to use whatever alias your management pack is using.  I have some code that will figure out what alias is being used for a particular management pack, but that will have to wait for a later post.

Creating the Group

Now that you know how to create the formula, you need the code to create the group.  We’re using the InsertCustomMonitoringObjectGroup method of the ManagementPack class.  We need to pass that a CustomMonitoringObjectGroup which is created with a group name, a group display name, and a formula.  The group name is created from a combination of a namespace and a name.  The namespace acts as the standard prefix for all objects in the management pack.  It will append that the the group name that we specify to build a complete name.  In the example below, I’m using a namespace of bwren.Groups and a group name of Gargantua.  The full name of the resulting group will be bwren.Groups.Gargantua.

With all that explanation, I’ll let the following code speak for itself.  This example creates an empty group, but all you would need to do to create a different membership rule is to replace the $formula variable with one of the examples above.

$mpName = 'bwren.Groups' 
$namespace = 'bwren.groups' 
$groupName = 'gargantua' 
$groupDisplayName = 'Gargantua'


$formula =  '<MembershipRule Comment="EMPTY_RULE_8eadaced-59c8-4ebc-a4e4-b8428a374442">' + ` 
            '<MonitoringClass>$MPElement[Name="SCIG!Microsoft.SystemCenter.InstanceGroup"]$</MonitoringClass>' + ` 
            '<RelationshipClass>$MPElement[Name="SCIG!Microsoft.SystemCenter.InstanceGroupContainsEntities"]$</RelationshipClass>' + ` 
            '<Expression>' + ` 
            '<SimpleExpression>' + ` 
            '<ValueExpression>' + ` 
            '<Value>True</Value>' + ` 
            '</ValueExpression>' + ` 
            '<Operator>Equal</Operator>' + ` 
            '<ValueExpression>' + ` 
            '<Value>False</Value>' + ` 
            '</ValueExpression>' + ` 
            '</SimpleExpression>' + ` 
            '</Expression>' + ` 
            '</MembershipRule>' 

$group = New-Object Microsoft.EnterpriseManagement.Monitoring.CustomMonitoringObjectGroup($namespace,$groupName,$groupDisplayName,$formula) 
$mp = Get-ManagementPack -name $mpName 
$mp.InsertCustomMonitoringObjectGroup($group)

As I ran out of time in the MMS session, I quickly showed a script that listed all performance monitors and their thresholds.  This came from the SharePoint Portal Server Management Pack Guide.  The only change I made was to comment out the command to export the results to a csv so the information would come scrolling across the screen.  I attached the script with that small change to this post.

There has been some question about how to get properties for monitoring objects with Command Shell.  I wanted to go into this in my MMS session last week, but I already ran out of time just trying to cram in all the topics I did cover.  Shouldn't be too tough to explain it here though.

If you use Get-MonitoringObject, you get a common set of properties for all objects.  Kind of like the following:

PS Monitoring:\OpsMgr01
>$mc = get-monitoringClass -name Microsoft.Windows.Computer
>$mc | get-monitoringObject | where {$_.name -match 'web01'} 

Id               : 0844f543-762c-5800-794c-a72a1823ea96
PathName         : web01.greengargantua.com
DisplayName      : web01.greengargantua.com
ManagementMode   :
ManagementGroup  : gaira
HealthState      : Success
OperationalState : 

PS Monitoring:\OpsMgr01
>$mc = get-monitoringClass -name Microsoft.SQLServer.2005.Database 
>$mc | get-monitoringObject | where {$_.name -match 'blob'}
Id : dfd4fd2b-6c7f-6c9c-a1cd-4d074613e3fd PathName : opsmgr01.greengargantua.com%003bMSSQLSERVER%003ablob DisplayName : blob ManagementMode : ManagementGroup : gaira HealthState : Success OperationalState :

That example shows two objects that are completely different - a database and a computer - but we show an identical set of properties.  We know that there a bunch of properties unique to each of those classes, but where are they?

In order to display all properties for the object, you can use one of the Format CmdLets with * indicating you want all properties.  That results in the following: 

PS Monitoring:\OpsMgr01
>$mc = get-monitoringClass -name Microsoft.SQLServer.2005.Database
>$mc | get-monitoringObject | where {$_.name -match 'blob'} | fl * PathName : opsmgr01.bwren.com%003bMSSQLSERVER%003ablob UniquePathName : Microsoft.SQLServer.Database%003aopsmgr01.bwren.com%003bMSSQLSERVER%003bblob [Microsoft.SQLServer.Database].DatabaseName : blob [Microsoft.SQLServer.Database].RecoveryModel : FULL [Microsoft.SQLServer.Database].DatabaseAutogrow : True [Microsoft.SQLServer.Database].DatabaseSize : 2 [Microsoft.SQLServer.Database].LogAutogrow : True [Microsoft.SQLServer.Database].Updateability : READ_WRITE [Microsoft.SQLServer.Database].UserAccess : MULTI_USER [Microsoft.SQLServer.Database].Collation : SQL_Latin1_General_CP1_CI_AS [Microsoft.SQLServer.Database].LogSize : 1 [Microsoft.SQLServer.Database].Owner : BWREN\Administrator [System.Entity].DisplayName : blob Name : blob Path : opsmgr01.bwren.com;MSSQLSERVER DisplayName : blob FullName : Microsoft.SQLServer.Database:opsmgr01.bwren.com;MSSQLSERVER;blob IsManaged : True LastModified : 11/26/2007 7:07:20 AM HealthState : Success StateLastModified : 5/1/2008 8:39:45 PM IsAvailable : True AvailabilityLastModified : 5/3/2008 5:20:29 PM InMaintenanceMode : False MaintenanceModeLastModified : 5/1/2008 8:39:06 PM MonitoringClassIds : {10c1c7f7-ba0f-5f9b-c74a-79a891170934, cae6be07-6483-361b-710f-c7612e29fa7b} LeastDerivedNonAbstractMonitoringClassId : 10c1c7f7-ba0f-5f9b-c74a-79a891170934 Id : dfd4fd2b-6c7f-6c9c-a1cd-4d074613e3fd ManagementGroup : bwren ManagementGroupId : 80342cfb-a3e1-aa59-7eac-cb5b5f167c38

The properties specific to the individual class are preceded with the name of the class they came from surrounded by brackets.  I'll explain the details of that later.  For now, just know that you have to specify the entire thing to retrieve the property value.  Since PowerShell sees those brackets as special characters, you need to surround the entire property name with quotes. 

>$mc = get-monitoringClass -name Microsoft.SQLServer.2005.Database
>$mo = $mc | get-monitoringObject | where {$_.name -match 'blob'}
>$mo."[Microsoft.SQLServer.Database].LogAutogrow"

That syntax can actually get tricky, and I'll be honest that I haven't figured out how to get it working in all cases.  The other approach you can use is the Get-MonitoringObjectProperty CmdLet.  The name property from that CmdLet will be the simple name of the property. 

>$mc = get-monitoringClass -name Microsoft.SQLServer.2005.Database 
>$mo = $mc | get-monitoringObject | where {$_.name -match 'blob'}
>$mo | get-monitoringObjectProperty | sort parentElement,name | ft parentElement,name,value ParentElement Name Value ------------- ---- ----- Microsoft.SQLServer.Database Collation SQL_Latin1_General_CP1_CI_AS Microsoft.SQLServer.Database DatabaseAutogrow True Microsoft.SQLServer.Database DatabaseName blob Microsoft.SQLServer.Database DatabaseSize 2 Microsoft.SQLServer.Database LogAutogrow True Microsoft.SQLServer.Database LogSize 1 Microsoft.SQLServer.Database Owner BWREN\Administrator Microsoft.SQLServer.Database RecoveryModel FULL Microsoft.SQLServer.Database Updateability READ_WRITE Microsoft.SQLServer.Database UserAccess MULTI_USER System.Entity DisplayName blob

If you already know about the concept of base classes in OpsMgr, then you already know what that that ParentElement class is.  If not, let me provide a very brief description just so you at least have a little background information.If you want more details on this whole concept of class structures in OpsMgr, the Authoring Guide has complete documentation.

All classes in OpsMgr have a base class.  That base class may have a base class, and its base class may have a base class, etc.  All classes will eventually find their way up to System.Entity which is the only class with no base.  The object tree for the SQL 2005 Database class is shown below.

image

 

If a class has properties associated with it, then any classes that inherit from it (ie. they use that class as their base class) inherit its properties and any properties along the class tree.  There are no properties explicitly assigned to SQL 2005 Database.  Instead, those properties you see (Collation, DatabaseAutogroup, etc) are defined on the SQL Database class.  As you might imagine, SQL 2000 Database inherits from that same class so it inherits the same properties as SQL Database.

My apologies that I forgot to talk about this in my MMS session earlier today.  I did mention it to a couple of people after the session.  Derek Harkin came up with an interesting method for initiating maintenance mode from an agent.  No OpsMgr client of any kind required - just a simple script for the user to execute.  No need to reiterate the details here, have a look at Derek's blog entry.

As promised, here are all the samples that I am showing in my MMS session on Command Shell.  There are also a several samples I'm planning on mentioning but won't have time to show.  Hope it's helpful.

Given the length of the this, chances are pretty good that I have an error somewhere.  Please let me know if there's anything that doesn't work, if there's anything you think I forgot, or if you have any improvements to any of these scenarios. 

 

Management Groups

Add a management group connection:

> new-managementGroupConnection OpsMgr02

 

Change your default management group connection:

> new-defaultManagementGroupConnection OpsMgr02 $true

 

View all current management group connections:

> get-ManagementGroupConnection

or

> cd \
> dir

 

Management Packs

Export a specific management pack (sealed or unsealed):

> get-managementPack -name Microsoft.SQLServer.2005.Monitoring | export-managementPack -path c:\mp 

 

Export all management packs in a management group:

> get-managementPack | export-managementPack -path c:\mp

 

Install a management pack to multiple management groups:

> new-managementGroupConnection OpsMgr02
> cd \

> install-managementPack c:\mp\Contoso.MyManagementPack.mp

 

Export management pack from test environment, seal it, and import to two production management groups:

> get-managementPack -name bwren.MMS | export-managementPack -path c:\mp
> MPSeal.exe  Contoso.MyManagementPack.xml /I c:\mp /Keyfile c:\keys\contosoKeyPair.snk  /Company "Contoso"
> new-managementGroupConnection ggOpsMgr01.greenGargantua.com
> cd \ggOpsMgr01.greenGargantua.com
> install-managementPack c:\mp\bwren.mms.xml
> cd \OpsMgr01.bwren.com
> get-managementGroupConnection | where {$_.managementServerName -eq 'ggOpsMgr01.greenGargantua.com'} | remove-managementGroupConnection

 

Agents

Install an agent on a single computer:

> $ms = get-managementServer | where {$_.name -eq 'OpsMgr02.GreenGargantua.com'}
> install-agentByName -name 'srv01' -managementServer $ms

 

Install agents to a list of computers from a text file:

> $ms = get-managementServer | where {$_.name -eq 'OpsMgr02.GreenGargantua.com'}
> gc c:\scripts\agents.txt | foreach {install-agentByName -name $_ -managementServer $ms}

 

Schedule install of agents to a list of computers from a text file. 

Script: InstallAgents.ps1

param($file,$managementServer,$rmsServerName)

Add-PSSnapin "Microsoft.EnterpriseManagement.OperationsManager.Client";
Set-Location "OperationsManagerMonitoring::";
$mgConn = New-ManagementGroupConnection -connectionString:$rmsServerName
Set-Location $rmsServerName

$ms = get-managementServer | where {$_.name -eq $managementServer}
gc $file | foreach {install-agentByName -managementServer $ms -name $_}

Run from Task Scheduler:

C:\WINDOWS\system32\windowspowershell\v1.0\powershell.exe "c:\scripts\InstallAgents.ps1" -file:'c:\scripts\computers.txt' -managementServer:OpsMgr02 -rmsServerName:'OpsMgr01'

 

Discover all domain controllers and install an agent on each: 

> $ms = get-managementServer | where {$_.name -eq 'OpsMgr02.GreenGargantua.com'}
> $discoveryConfig = new-ldapQueryDiscoveryCriteria -domain contoso.com -ldapQuery "(primaryGroupID=516)"
> $discoveryResult = start-discovery -managementServer $ms -windowsDiscoveryConfiguration $discoveryConfig
> $installResult = install-agent -managementServer $ms -agentManagedComputer $discoveryResult.CustomMonitoringObjects
> $installResult.MonitoringTaskResults

 

Display the proxy setting for all agents with a particular string in the computer name:

> get-agent | where {$_.computerName -match 'dc'} | ft name,proxyingEnabled

 

Enable the proxy setting for all agents with a particular string in the computer name:

> $agents = get-agent | where {$_.computerName -match 'dc'}
> $agents | foreach {$_.ProxyingEnabled = $true}
> $agents | foreach {$_.ApplyChanges()}

 

Move all agents on a subnet to a different management server:

> $ms = get-managementServer | where {$_.name -eq 'OpsMgr02.greenGargantua.com'}
> $agents = get-agent | where {$_.IPAddress -match '10.2.*.*'}
> $agents | set-managementServer -primaryManagementServer $ms

 

Security

Get list of roles a user belongs to:

> get-userRole | where {$_.users -match 'iggy'} | ft name

 

Add list of users from a text file to a user role:

Script: AddUsersFromFile.ps1

param($file)

 

$entries = import-csv -path $file
foreach ($entry in $entries) {
    $role = get-userRole | where {$_.name -eq $entry.role}
    add-userToUserRole -userRole $role -user $entry.user
}

Sample Text File: users.txt

role,user
Marketing,contoso\iggy
Marketing,contoso\johnny
Finance,contoso\sid
IT,contoso\trent

Running Script:

> c:\scripts\AddUsersFromFile -file c:\scripts\users.txt

   

Monitoring Classes and Objects

List all monitoring classes:

> get-monitoringClass | ft name

 

List all SQL 2005 databases:

> (get-monitoringClass -name Microsoft.SQLServer.2005.Database) | get-monitoringObject | ft name

OR

> (get-monitoringClass | where {$_.displayName -eq 'SQL 2005 DB'} | get-monitoringObject | ft name

 

List all Windows computers and their IP addresses:

> get-monitoringClass -name Microsoft.Windows.Computer | get-monitoringObject | foreach {$_."[Microsoft.Windows.Computer].IPAddress"}

 

List all monitoring classes in the SQL Server 2005 Discovery management pack

> $mp = get-managementPack -name Microsoft.SQLServer.2005.Discovery
> $mp | get-monitoringClass | ft name

 

 

Display the proxy settings for all agents holding a particular class:

> $mc = get-monitoringClass -name 'Microsoft.Windows.Server.AD.DomainControllerRole'
> $
mos = $mc | get-monitoringObject
> $mo | foreach {get-agent | where {$_.computerName -eq $mo.name}} | ft name,proxyingEnabled

 

Enable the proxy settings for all agents holding a particular class:

> $mc = get-monitoringClass -name 'Microsoft.Windows.Server.AD.DomainControllerRole'
> $mos = $mc | get-monitoringObject
> foreach ($mo in $mos) {
   $agent = get-agent | where {$_.computerName -eq $mo.displayName}
   $agent.proxyingEnabled = $true
   $agent.applyChanges()
}

 

Maintenance Mode

Set maintenance mode for a single object: 

> $StartTime = (get-Date '4/1/2008 22:00').ToUniversalTime()
> $EndTime = (get-Date '4/2/2008 2:00').ToUniversalTime()
> $mc = get-monitoringClass -name Microsoft.SQLServer.2005.Database
> $mo = get-monitoringObject -monitoringClass $mc | where {$_.name -eq 'MyDatabase'}
> new-maintenanceWindow -monitoringObject $mo -startTime $StartTime -endTime $EndTime -reason PlannedOther -comment "Scheduled maintenance."

 

Set maintenance mode for an object and all of its contained objects:

> $StartTime = (get-Date '4/1/2008 22:00').ToUniversalTime()
> $EndTime = (get-Date '4/2/2008 2:00').ToUniversalTime()
> $mc = get-monitoringClass -name Microsoft.Windows.Computer
> $mo = get-monitoringObject -monitoringClass $mc | where {$_.name -eq 'srv01'}
> $mo.ScheduleMaintenanceMode($StartTime,$EndTime,PlannedOther,"Nightly reboot.","Recursive")

 

Schedule maintenance mode for all computers in a group (uses GroupMM.ps1):

> C:\WINDOWS\system32\windowspowershell\v1.0\powershell.exe "c:\scripts\GroupMM.ps1" -groupName:'Nightly Reboots' -hours:2 -rmsServerName:'OpsMgr01' -startMM:$true

 

View maintenance mode history for an object:

> $mc = get-monitoringClass -name Microsoft.Windows.Computer
> $mo = get-monitoringObject -monitoringClass $mc | where {$_.name -eq 'srv01'}
> $mo | get-maintenanceWindow -history

 

Tasks

Run task to enable audit collection on all agents:

> $task = get-task | where {$_.name -eq 'Microsoft.SystemCenter.EnableAuditCollectionService'}
> $mc = get-monitoringClass | where {$_.name -eq 'Microsoft.SystemCenter.HealthService'}
> $mc | get-monitoringObject | foreach {start-task -task $task -targetMonitoringObject $_}

 

Data

View all unresolved alerts:

> get-alert -criteria "ResolutionState <> 255"

 

View all alerts grouped by severity and name

> get-alert -criteria "ResolutionState <> 255" | sort severity,name | group severity,name

 

View alerts from a particular rule group by managed object:

> get-alert -criteria "ResolutionState <> 255" | where {$_.name -match 'Script or Executable Failed to run'} | group monitoringObjectDisplayName

 

Change resolution state of alerts from a particular rule group by managed object:

> get-alert -criteria "ResolutionState <> 255" | where {$_.name -match 'Script or Executable Failed to run'} | group monitoringObjectDisplayName | foreach {
>> $_.ResolutionState = 50
>> $_.update("Comment")
>>}
>>

 

Resolve all alerts generated by rules as opposed to monitors:

> get-alert -criteria "ResolutionState <> 255 and IsMonitorAlert = 'False'" | resolve-Alert

 

Reset health for a monitor called "Manual monitor" on all objects of the class "Contoso.MyCustomClass" currently in an Error state.

> $mon = get-monitor | where {$_.displayName -eq 'Manual monitor'}
> $mc = get-monitoringClass -name Contoso.MyCustomClass
> $mc | get-monitoringObject | where {$_.HealthState -eq 'Error'} | foreach {$_.ResetMonitoringState($mon)}

 

Performance Data

Extract processor utilization data for all computers for the month of March 2008 to a comma delimited file.

> $startTime = get-date '3/1/2008'
> $endTime = get-date '3/31/2008'
> $pc = get-performanceCounter -criteria: "ObjectName='Processor' and CounterName='% Processor Time' and MonitoringObjectPath='web02.bwren.com'"
> get-performanceCounterValue -startTime $startTime -endTime $endTime -performanceCounter $pc | export-csv c:\scripts\mom\perf.csv

 

 

Management Pack Elements

List all rules by management pack:

> get-rule | select @{name="MP";expression={foreach-Object {$_.GetManagementPack().DisplayName}}},DisplayName | sort mp,displayName

 

List all monitors in a specific management pack:

> (get-managementPack -name Microsoft.SQLServer.2005.Monitoring) | get-monitor | sort displayName | ft displayName

 

List all disabled discoveries:

> get-discovery | where {$_.enabled -eq 'false'} | ft displayName

 

View the display name, category, and enabled status of all performance collection rules in the SQL Server 2005 Monitoring management pack:

> get-rule -managementPack $mp | where {$_.category -eq 'PerformanceCollection'} | ft displayName,category,enabled

More Posts Next page »
 
Page view tracker