Archive for June 2009

PowerShell and the Service Manager Type Environment   Leave a comment

Service Manager, like Operations Manager, has a dynamic type system.  ManagementPacks define types, called classes, which define the actual data that you want to keep track of.  You declare a class which may or may not be based on another class and you can add properties to your class, and manipulate it in a number of ways.  Once you’ve imported your management pack, you’ll have access to that new class, and can create instances of that class.  There are two discrete categorizations of types.  The first is a simple class, and the more complex is called a type projection.  In this blog, we’ll discuss how to find out what Service Manager stores as simple classes and how to discover them.

It’s important to note that these are not .NET types, these are types as defined in the Service Manager environment.  When you actually create instances of these types, they are returned as instances of the .NET type of Microsoft.EnterpriseManagement.Common.EnterpriseManagementObject.  That object has a property called PropertyCollection which is where the actual data is held.  I’ll talk more about that in a future blog, but for now, what you should know is that Service Manager has it’s own types which are created in Management Packs.

To get access to the list of classes that have been imported to the Service Manager environment, we’ll use the EnterpriseManagementGroup object again (see my previous posts).  The EnterpriseManagementGroup object has an interface named "EntityTypes", which has a number of methods that we’ll investigate.  First, we’ll create an EnterpriseManagementGroup, and take a look at the EntityTypes interface.

PS> $EMG = new-object Microsoft.EnterpriseManagement.EnterpriseManagementGroup localhost
PS> $EMG.EntityTypes|get-member

   TypeName: Microsoft.EnterpriseManagement.EntityTypeManagement

Name                       MemberType Definition
----                       ---------- ----------
Equals                     Method     bool Equals(System.Object obj)
GetCategories              Method     System.Collections.Generic.IList[Micro...
GetCategory                Method     Microsoft.EnterpriseManagement.Configu...
GetCategoryList            Method     System.Collections.Generic.List[Micros...
GetChildEnumerations       Method     System.Collections.Generic.IList[Micro...
GetClass                   Method     Microsoft.EnterpriseManagement.Configu...
GetClasses                 Method     System.Collections.Generic.IList[Micro...
GetClassesList             Method     System.Collections.Generic.List[Micros...
GetEnumeration             Method     Microsoft.EnterpriseManagement.Configu...
GetEnumerationList         Method     System.Collections.Generic.List[Micros...
GetEnumerations            Method     System.Collections.Generic.IList[Micro...
GetHashCode                Method     int GetHashCode()
GetRelationshipClass       Method     Microsoft.EnterpriseManagement.Configu...
GetRelationshipClasses     Method     System.Collections.Generic.IList[Micro...
GetRelationshipClassesList Method     System.Collections.Generic.List[Micros...
GetTopLevelEnumerations    Method     System.Collections.Generic.IList[Micro...
GetType                    Method     type GetType()
GetTypeProjection          Method     Microsoft.EnterpriseManagement.Configu...
GetTypeProjectionList      Method     System.Collections.Generic.List[Micros...
GetTypeProjections         Method     System.Collections.Generic.IList[Micro...
ToString                   Method     string ToString()

The methods break up in two big groups: The first group returns all various different objects that you can define in a management pack (those objects which have to do with types, anyway). That list is:


The other methods on this interface take arguments and return a reduced set of results based on the arguments. It is important to note that the Service Manager environment caches most of this configuration information, so when we get all of them, it doesn’t result in lots of trips to the database. That’s good news from a performance perspective.  From a PowerShell perspective, it means that we can grab all the data and use the PowerShell filtering capabilities rather than bothering with the other available methods.

If we want to see all the different classes that Service Manager knows about, we can do that easily:

PS> $EMG.EntityTypes.GetClasses() 

PropertyCollection     : {DisplayName}
Base                   :
Hosted                 : False
Singleton              : False
Extension              : False
OptimizationCollection : {}
FullTextSearchable     :
XmlTag                 : ClassType
Abstract               : True
Accessibility          : Public
ManagementGroup        : WIN-752HJBSX24M
ManagementGroupId      : 386f5f57-9f7a-3c6b-1f53-ccc02d6206d4
Name                   : System.Entity
Id                     : ac5cddfc-a96a-ee99-745d-ec74845f53f6
DisplayName            : Object
Description            : All objects
LanguageCode           : ENU
Comment                :
Status                 : Unchanged
LastModified           : 5/14/2009 11:56:48 PM
TimeAdded              : 5/14/2009 11:56:48 PM

PropertyCollection     : {}
Base                   : ManagementPackElementUniqueIdentifier=ac5cddfc-a96a-ee
Hosted                 : False
Singleton              : False
Extension              : False
. . .

Wow! There’s a lot here – let’s find out how many:

PS> $Classes = $EMG.EntityTypes.GetClasses()
PS> $Classes.Count 231

We should determine the .NET type as well, as we can use this when we create our formatting instructions

PS> $classes[0].gettype().fullname

With a result as large as this, it makes more sense to see as a table, rather than a list and I’ll select the first 5, just to cut down on space, so now we have:

PS> $Classes|select-object -first 5| format-table Abstract,Name,DisplayName -au
Abstract Name                       DisplayName
-------- ----                       -----------
    True System.Entity              Object
    True System.AdminItem           Admin Item
   False System.Announcement.Config Config
   False System.Announcement.Item   Announcement
    True System.Collections         Collections

We can see that this supports the PowerShell filters as well, let’s find all the types which pertain to printers.  The first approach would be to select only those objects whose name matches print!

PS> $EMG.EntityTypes.GetClasses()|?{$ –match "print"}|
>> format-table -auto Abstract,Name,DisplayName

Abstract Name DisplayName
-------- ---- -----------
True System.Printer Printers
False Microsoft.AD.Printer Active Directory Printers

The objects returned work well with the PowerShell environment.  We can invoke the other methods on the EntityTypes interface:

PS> $emg.EntityTypes.GetCategories().Count
PS> $emg.EntityTypes.GetCategories()|select-object -first 5|format-table Name,DisplayName -auto

Name                                                                  DisplayName
----                                                                  -----------

PS> $emg.EntityTypes.GetClasses().Count
PS> $emg.EntityTypes.GetClasses()|select-object -first 5|format-table Name,DisplayName -auto

Name                       DisplayName 
----                       ----------- 
System.Entity              Object      
System.AdminItem           Admin Item  
System.Announcement.Config Config      
System.Announcement.Item   Announcement
System.Collections         Collections 

PS> $emg.EntityTypes.GetEnumerations().Count
PS> $emg.EntityTypes.GetEnumerations()|select-object -first 5|format-table Name,DisplayName -auto

Name                                                        DisplayName          
----                                                        -----------          
ActivityAreaEnum.Messaging.Client                           Client               
ServiceManager.ConfigurationManagement.WindowsPrintersTasks Windows Printer Tasks
IncidentSourceEnum.DCM                                      SCCM (DCM)           
ActivityStageEnum.Develop                                   Develop              

PS> $emg.EntityTypes.GetRelationshipClasses().Count
PS> $emg.EntityTypes.GetRelationshipClasses()|select-object -first 5|format-table Name,DisplayName -auto

Name                                DisplayName                     
----                                -----------                     
System.ComputerPrimaryUser          Computer Primary User           
System.ConfigItemContainsConfigItem Config Item Contains Config Item
System.ConfigItemHasFileAttachment  Config Item Has File Attachment 
System.ConfigItemImpactsCustomers   Config Item Impacts Customers   
System.ConfigItemOwnedByUser        Config Item Owned By User       

PS> $emg.EntityTypes.GetTopLevelEnumerations().Count
PS> $emg.EntityTypes.GetTopLevelEnumerations()|select-object -first 5|format-table Name,DisplayName -auto

Name                                                                          DisplayName         
----                                                                          -----------         
System.Internal.ManagementPack                                                Management Pack     
System.WorkItem.ActionLogEnum                                                 Action Log Enum     
ChangeManagement.CreateTask                                                   Create Task         
Microsoft.EnterpriseManagement.ServiceManager.UI.Authoring.AllObjectTemplates All Object Templates

PS> $emg.EntityTypes.GetTypeProjections().Count
PS> $emg.EntityTypes.GetTypeProjections()|select-object -first 5|format-table Name,DisplayName -auto

Name DisplayName
---- -----------

It looks like we have reasonable output for everything except TypeProjections.  By looking at the count, I can tell that we have some sort of results, so let’s take a closer look at the object:

PS> $emg.EntityTypes.GetTypeProjections()[0]

Key                                     Value
---                                     -----
SyncStatus                              {}

That’s not terribly useful, perhaps get-member will help me.

PS> $emg.EntityTypes.GetTypeProjections()|get-member

   TypeName: Microsoft.EnterpriseManagement.Configuration.ManagementPackTypeProjection

Name                MemberType            Definition
----                ----------            ----------
CreateNavigator     Method                System.Xml.XPath.XPathNavigator Cr...
Equals              Method                bool Equals(System.Object obj)
GetCategories       Method                System.Collections.Generic.IList[M...
GetDisplayString    Method                Microsoft.EnterpriseManagement.Con...
GetEnumerator       Method                System.Collections.Generic.IEnumer...
GetFolders          Method                Microsoft.EnterpriseManagement.Con...
GetHashCode         Method                int GetHashCode()
GetImageReferences  Method                System.Collections.Generic.IEnumer...
GetKnowledgeArticle Method                Microsoft.EnterpriseManagement.Con...
GetManagementPack   Method                Microsoft.EnterpriseManagement.Con...
GetType             Method                type GetType()
Reconnect           Method                System.Void Reconnect(Microsoft.En...
ToString            Method                string ToString()
WriteXml            Method                System.Void WriteXml(System.Xml.Xm...
Item                ParameterizedProperty Microsoft.EnterpriseManagement.ITy...
Accessibility       Property              Microsoft.EnterpriseManagement.Con...
Alias               Property              System.String Alias {get;}
Comment             Property              System.String Comment {get;set;}
ComponentCollection Property              System.Collections.Generic.IList`1...
Description         Property              System.String Description {get;set;}
DisplayName         Property              System.String DisplayName {get;set;}
Id                  Property              System.Guid Id {get;}
LanguageCode        Property              System.String LanguageCode {get;set;}
LastModified        Property              System.DateTime LastModified {get;...
ManagementGroup     Property              Microsoft.EnterpriseManagement.Ent...
ManagementGroupId   Property              System.Guid ManagementGroupId {get;}
Name                Property              System.String Name {get;}
Parent              Property              Microsoft.EnterpriseManagement.ITy...
Status              Property              Microsoft.EnterpriseManagement.Con...
TargetConstraint    Property              Microsoft.EnterpriseManagement.Con...
TargetEndpoint      Property              Microsoft.EnterpriseManagement.Con...
TargetType          Property              Microsoft.EnterpriseManagement.Con...
TimeAdded           Property              System.DateTime TimeAdded {get;set;}
Type                Property              Microsoft.EnterpriseManagement.Con...
TypeProjection      Property              Microsoft.EnterpriseManagement.Con...
XmlTag              Property              System.String XmlTag {get;}

Oho!  it looks like this object has an enumerator.  This means that when PowerShell attempts to format the object, it will call the enumerator and format the enumerated contents (rather than the object).  We can suppress this in PowerShell by specifying the –EXPAND parameter with format-table:

PS> $emg.EntityTypes.GetTypeProjections()|select-object -first 5|
>> format-table -Expand coreonly Name,DisplayName -au

Name                                                          DisplayName
----                                                          -----------
System.NotificationChannel.SMTP.ProjectionType                SMTP Projection Type
System.User.Projection                                        User Projection

That’s better! Although I have a feeling that this really isn’t a good long term solution, perhaps we’ll deal with this in a future posting.

Now that we have a set of these methods we can invoke, it’s a great opportunity to build a PowerShell V2 Module.  We can easily convert these bits of script into functions and aggregate those functions into a module.  All I need to do is put my module (as a .psm1 file) in the right place (see PowerShell V2 documentation on Modules for more help) and call import-module!

Here’s the script file:

$SMDIR = "C:\Program Files\Microsoft System Center\Service Manager 2010"
$SMDLL = "${SMDIR}\SDK Binaries\Microsoft.EnterpriseManagement.Core.dll"
$EMGTYPE = "Microsoft.EnterpriseManagement.EnterpriseManagementGroup"

# Before anything load the Service Manager core dll
[reflection.assembly]::LoadFile( $SMDLL ) | out-null

# Create an EnterpriseManagementGroup object
function New-EMG
   param ( $ComputerName = "localhost" ) 
   new-object $EMGTYPE $ComputerName

# Return the Categories
function Get-SMCategory
    param ( $CategoryName )
    if ( ! $EMG.IsConnected ) { $EMG.Reconnect() }
    $EMG.EntityTypes.GetCategories()|?{$_.Name -match $CategoryName }
# Return the Classes
function Get-SMClass
    param ( $ClassName )
    if ( ! $EMG.IsConnected ) { $EMG.Reconnect() }
    $EMG.EntityTypes.GetClasses()|?{$_.Name -match $ClassName }
# Return the RelationshipClasses
function Get-SMRelationshipClass
    param ( $RelationshipClassName )
    if ( ! $EMG.IsConnected ) { $EMG.Reconnect() }
    $EMG.EntityTypes.GetRelationshipClasses()|?{$_.Name -match $RelationshipClassName }
# Return the TopLevelEnumerations
function Get-SMTopLevelEnumeration
    param ( $TopLevemEnumerationName )
    if ( ! $EMG.IsConnected ) { $EMG.Reconnect() }
    $EMG.EntityTypes.GetTopLevelEnumerations()|?{$_.Name -match $TopLevemEnumerationName }
# Return the TypeProjections
function Get-SMTypeProjection
    param ( $TypeProjetionName )
    if ( ! $EMG.IsConnected ) { $EMG.Reconnect() }
    $EMG.EntityTypes.GetTypeProjections()|?{$_.Name -match $TypeProjetionName }
# We want to have an EMG in our environment!

Notice also that I added a parameter to the functions so I can pass in a string and reduce the result without having to always add my own where-object pipeline.  Notice further that I created a global instance of the EnterpriseManagementGroup, that way I can use it outside of the module.  I’ll use this as the contents for my EntityTypes.PSM1 file and import the module. 

    You should note that this assumes an installation on the Server machine only.  If you want to run these on a machine where only the console is installed, you’ll need to load the SMDLL a little differently.   You would need to do the following instead:


    And you will also need to provide the name of the Service Manager server system when you create your EnterpriseManagementGroup object by calling new-EMG:

    $GLOBAL:EMG = new-EMG servername

On to the module!

PS> get-childitem C:\users\Administrator\Documents\WindowsPowerShell\Modules\EntityTypes

    Directory: C:\users\Administrator\Documents\WindowsPowerShell\Modules\EntityTypes

Mode                LastWriteTime     Length Name
----                -------------     ------ ----
-a---         6/19/2009   2:27 PM       1726 EntityTypes.psm1

PS> get-module -list

ModuleType Name                      ExportedCommands
---------- ----                      ----------------
Script     EntityTypes               {}
Manifest   BitsTransfer              {}
Manifest   PSDiagnostics             {}

PS> import-module entitytypes

and we can see that my commands are all present!

PS> get-module entitytypes|fl

Name              : entitytypes
Path              : C:\Users\Administrator\Documents\WindowsPowerShell\Modules\entitytypes\entitytypes.psm1
Description       :
ModuleType        : Script
Version           : 0.0
NestedModules     : {}
ExportedFunctions : {Get-Category, Get-Class, Get-RelationshipClass, Get-TopLevelEnumeration...}
ExportedCmdlets   : {}
ExportedVariables : {}
ExportedAliases   : {}

And I can use them just like a cmdlet:

PS> get-class system.user$|ft abstract,name,displayname -au

Abstract Name        DisplayName
-------- ----        -----------
    True System.User Users

In my next post, I’ll discuss the Service Manager instance space and create cmdlets to use against the actual data that we store in Service Manager.


Posted June 22, 2009 by jtruher3 in ServiceManager

Even more with Management Packs   Leave a comment

Last post, I went through the process of exporting management packs, so we can see what they do and what they define.  In this post, I’ll discuss importing management packs.  In order to get any benefit from a Management Pack (MP), it needs to be added to the Service Manager platform.  The process of adding an MP to the system is called "Importing".   For this example, we’ll use just a very simple management pack (the contents aren’t really interesting, but to go through the exercise we need one of these).

<ManagementPack ContentReadable="true" SchemaVersion="1.1" 
    <Name>A Simple ManagementPack</Name>
      <Reference Alias="System">
        <ClassType ID="Simple.Class" Accessibility="Public" Abstract="false"  
                       Base="System!System.Entity" Hosted="false"  
                       Singleton="false" Extension="false">
          <Property ID="Id"     Type="guid"   Key="true"  Required="true"  />
          <Property ID="Name"   Type="string" Key="false" Required="false" 
                       CaseSensitive="false" MaxLength="256" MinLength="0" />
          <Property ID="Value1" Type="string" Key="false" Required="false"  
                       CaseSensitive="false" MaxLength="256" MinLength="0" />
          <Property ID="Value2" Type="string" Key="false" Required="false"  
                       CaseSensitive="false" MaxLength="256" MinLength="0" />
    <LanguagePack ID="ENU" IsDefault="true">
        <DisplayString ElementID="Simple.Class">
          <Name>Simple Class</Name>
          <Description>A simple class declaration</Description>

The Service Manager console has a way to import this, of course, here’s what it looks like:

But you can also do this from PowerShell with a few of simple lines of script

# the two .NET types, we'll need to create
$EMGTYPE = "Microsoft.EnterpriseManagement.EnterpriseManagementGroup"
$MPTYPE  = "Microsoft.EnterpriseManagement.Configuration.ManagementPack"
# Create a connection to the Data Access Service
$EMG = new-object ${EMGTYPE} localhost
# Create a management pack object based on the management pack file and 
# connection to the Data Access Service
$MP = new-object ${MPTYPE} c:\temp\Simple.ManagementPack.xml,$EMG
# Call the import method on the ManagementPacks interface - all done!

As you can see, it’s pretty straightforward.  However, this isn’t the whole story.  If you notice in the XML above, there’s a section for "References", this section of the MP allows you to declare that your MP relies on other MPs for class definitions, etc.  This means that if those references aren’t installed, you’re MP isn’t going to work.  We go along way to keep bad data out of the system, so if you try to import an MP which references an MP which isn’t present on the system, then that import will fail.  When you’re considering a single MP, working out these references may not be to bad, but if you have a number of MPs that you want to import and some of them depend on other new imported MPs, you’ve got to get the order correct, or you won’t be able to import.  Most of the time, you’ll wind up inspecting those sections of the MPs, create your order, and finally import the MPs.  (I’ve seen a number of batch files that do this very thing, so I know that this happens).  That’s no good as it’s an awful waste of time and error prone as well.  I’ve created a script to go through any number of MPs and figure out what order they should be installed, and then install them!  The script is built so you can pipe the output of get-childitem to it. 

From a pseudo-code perspective, here’s what we need to do

Find out what MPs are already installed
and then
FOREACH of MPs to install
   retrieve the references
   FOREACH reference
         check to see if the referenced MPs is not already installed (or will be installed)
         IF the needed MP is not installed, skip for now
         IF all the references are
             installed and 
             the version is compatible and 
             the referenced MP is sealed and 
             the keytoken matches
             add this MP to the list of MPs to which we'll be able to install
             remove this MP from the list of MPs that we want to install
             add it to the list of MPs that are already installed (since it will be)
             since the list of MPs to install has now changed, restart the main loop

If there are any MPs left in the list to install, it means that we weren’t able to resolve the references, so we won’t be able to install that MP.  Note that the failure can be for any reason, it could rely on an unsealed MP (I don’t want to discuss sealing here, suffice it to say that sealing an MP makes it immutable, this way we know the things that we rely on won’t change), the reference could be missing, it could require the wrong version, or it could have an incorrect KeyToken.   There really isn’t much automatic remediation that we can do, but we can report what went wrong.

So, now that we have a mechanism to put MPs in order, let’s see how it looks.

For our simple test, we’ll create 6 MPs, each one of them dependent on the next MP (from an alphabetical point of view). 

  1. SimpleMP.A depends on SimpleMP.B
  2. SimpleMP.B depends on SimpleMP.C
  3. SimpleMP.C depends on SimpleMP.D
  4. SimpleMP.D depends on SimpleMP.L
  5. SimpleMP.L depends on SimpleMP.M
  6. SimpleMP.M depends on System.Library

this means that the need to be installed in the opposite order, M installed before L, etc. 

First we’ll start by importing the XML file, since XML files can’t be sealed, we should get an error for every MP except the only one that can be installed (SimpleMP.M, since it relies on System.Library).

Now we can see where we have problems, and which MPs we can install.  Because I’m importing unsealed MPs, the references that I had can’t be satisfied.  Only SimpleMP.M can be installed, since it has only one reference to a sealed MP (System.Library).  Note also, that SimpleMP.L can’t be installed because it references SimpleMP.M which is unsealed.  The other MPs can’t be installed because their references aren’t there.  Now, let’s try this again with sealed MPs.

PS> Get-ChildItem *.mp

    Directory: Microsoft.PowerShell.Core\FileSystem::C:\test

Mode                LastWriteTime     Length Name
----                -------------     ------ ----
-a---          4/2/2009  12:49 PM       4608
-a---          4/2/2009  12:49 PM       4608
-a---          4/2/2009  12:49 PM       4608
-a---          4/2/2009  12:49 PM       4608
-a---          4/2/2009  12:49 PM       4608
-a---          4/2/2009  12:49 PM       4608

If we tried to import these MPs in alphabetical order, only the last one would succeed, so we could import them by creating a script which does the import explicitly, one at a time, but we can use our script to do it in one fell swoop.  This means that we don’t really have to work out the dependencies manually – automation – that’s the key!

PS> get-childitem *.mp|./Import-ManagementPack -whatif 
WhatIf: Importing Management Pack SimpleMP.M
WhatIf: Importing Management Pack SimpleMP.L
WhatIf: Importing Management Pack SimpleMP.D
WhatIf: Importing Management Pack SimpleMP.C
WhatIf: Importing Management Pack SimpleMP.B
WhatIf: Importing Management Pack SimpleMP.A

Wahoo!  Even though the MPs have dependencies in reverse to the order returned by get-childitem, they’ll be installed in the correct order.  Granted, this example is pretty simple and the dependencies are straightforward, however, this script should handle pretty complicated sets of dependencies.

When actually coding this script, I decided that I wanted to make sure that I could be a bit more flexible than what was strictly possible from within Service Manager.  So I added options to avoid checking for the correct version, whether the MP in the reference is sealed and if the KeyToken is correct.    You still won’t be able to actually import if some of these things are wrong, but you’ll see the order if everything was correct and proper.  I also added -Verbose and -Debug parameters as well as a -WhatIf parameter which allows you to not actually Import the MPs, but will tell you what order they would be installed.  The script it a little on the larger size (about 200 lines), so I’m not going to walk through the script in this blog, but the code is fairly well commented. 

Here’s a link to the script:

Posted June 1, 2009 by jtruher3 in ServiceManager