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:
GetCategories GetClasses GetEnumerations GetRelationshipClasses GetTopLevelEnumerations GetTypeProjections
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
99-745d-ec74845f53f6
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 Microsoft.EnterpriseManagement.Configuration.ManagementPackClass
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()|?{$_.name –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
285
PS> $emg.EntityTypes.GetCategories()|select-object -first 5|format-table Name,DisplayName -auto
Name DisplayName
---- -----------
ServiceManager.ActivityManagement.EditActivity.Task.FlagCategory
ServiceManager.ActivityManagement.Library.ApprovalEnumVisibleCategory
ServiceManager.ActivityManagement.Library.DecisionEnumVisibleCategory
ServiceManager.ActivityManagement.Library.DecisionEnumCategory
ServiceManager.ActivityManagement.Library.ApprovalEnumCategory
PS> $emg.EntityTypes.GetClasses().Count
231
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
414
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)
System.Knowledge.CategoryEnum.Software
ActivityStageEnum.Develop Develop
PS> $emg.EntityTypes.GetRelationshipClasses().Count
118
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
72
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
System.Knowledge.CategoryEnum
PS> $emg.EntityTypes.GetTypeProjections().Count
43
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 ---- ----------- Microsoft.SystemCenter.LinkingFramework.SyncStatus.Projection System.LinkingFramework.DataConnector.Projection OpsMgrConnector.Config.Projection 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:
# MODULE VARIABLES $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 # MODULE FUNCTIONS # 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! $GLOBAL:EMG = new-EMG
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:
[Reflection.Assembly]::LoadWithPartialName("Microsoft.EnterpriseManagement.Core")
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.