This is a temporary post that was not deleted. Please delete this manually. (443c23da-13d3-4098-bd55-5b54c1df3e73 – 3bfe001a-32de-4114-a6b4-4005b770f6d7)
Temporary Post Used For Theme Detection (ae17e6a2-25fb-4a0c-a331-08054d3157f1 – 3bfe001a-32de-4114-a6b4-4005b770f6d7) Leave a comment
Retrieving projection data with PowerShell Leave a comment
There are a number of cmdlets in the SMLets project (http://smlets.codeplex.com) which retrieve data from Service Manager. To reduce the amount of data in getting simple instances from Service Manager, Get-SCSMObject provides a filter parameter which lets you provide a simple property/operator/value triad to reduce the amount of data that is retrieved from the CMDB. This is really helps performance because the filtering happens on the server. We can see the difference pretty easily:
PS# measure-command { get-scsmobject -Class $incidentclass | ?{ $_.Title -like "Ipsum*" } } Days : 0 Hours : 0 Minutes : 0 Seconds : 1 Milliseconds : 460 Ticks : 14609004 TotalDays : 1.69085694444444E-05 TotalHours : 0.000405805666666667 TotalMinutes : 0.02434834 TotalSeconds : 1.4609004 TotalMilliseconds : 1460.9004 PS# measure-command { get-scsmobject -Class $incidentclass -filter { Title -like "Ipsum*" } } Days : 0 Hours : 0 Minutes : 0 Seconds : 0 Milliseconds : 134 Ticks : 1341265 TotalDays : 1.5523900462963E-06 TotalHours : 3.72573611111111E-05 TotalMinutes : 0.00223544166666667 TotalSeconds : 0.1341265 TotalMilliseconds : 134.1265
In total time, this might not be impressive, but from a percentage perspective, it is. In this case, filtering on the server cuts the operation time by 90%, which is pretty substantial. However, simple instances are small potatoes in comparison to what we can save if we implement a filter on projection retrieval.
There is also a filter parameter on Get-SCSMObjectProjection, but it only allows you to filter against properties of the seed object, there’s no way to query the relationships in this filter. However, since much of the interesting information about a projection is the relationship data, so a simple filter isn’t as much help as it is for simple instances. Because I wanted to be sure that there was at least some way that you could query against the relationships, I included a criteria parameter which takes an ObjectProjectionCriteria, but left the creation of this criteria as “an exercise for the reader”. I’ve had a few requests for this, so I thought it would be good to build a way to easily create this criteria based on the projection. Behaviorally, I wanted to provide a similar experience to that of the filter’s property/operator/value trio, so the filter that I created for projections has the same basic shape, but the property part of the trio has a different look.
The property part of the filter is broken into 2 pieces, the relationship (as expressed in the alias) and the property on that relationship. If we look at the System.WorkItem.Incident.View.ProjectionType we see the following structure:
PS# get-scsmtypeprojection incident.view.projection ProjectionType: System.WorkItem.Incident.View.ProjectionType ProjectionSeed: System.WorkItem.Incident Components: Alias TargetType TargetEndPoint ----- ---------- --------------- AffectedUser System.User RequestedWorkItem AssignedUser System.User AssignedWorkItem
This projection has two components “AffectedUser” and “AssignedUser”. With this script, I can construct a filter like this:
AssignedUser.DisplayName = 'Joe User'
which will check the DisplayName property of the System.User object which is the end point of the relationship. I also wanted to support multiple queries, so I added support for -AND which allows you to create multiple property/operator/value statements.
The savings in retrieving projection data is substantial. Here’s a query which retrieves incidents which have a priority of 2 and have a related work-item which has a DisplayName which is equal to MA37. Filtering in the query is 200 times faster.
PS# measure-command { >> .\new-scsmProjectionCriteria.ps1 $ipfull.__Base -filter { >> priority = 2 -and RelatedWorkItems.DisplayName -eq "MA37" } -result >> } Days : 0 Hours : 0 Minutes : 0 Seconds : 0 Milliseconds : 625 Ticks : 6258242 TotalDays : 7.24333564814815E-06 TotalHours : 0.000173840055555556 TotalMinutes : 0.0104304033333333 TotalSeconds : 0.6258242 TotalMilliseconds : 625.8242
PS# measure-command { Get-SCSMObjectProjection -ProjectionObject $ipfull | >> ?{ $_.priority -eq "2" -and ($_.RelatesToWorkItem_ |?{$_.DisplayNAme -eq "MA37" })} } Days : 0 Hours : 0 Minutes : 2 Seconds : 5 Milliseconds : 888 Ticks : 1258883302 TotalDays : 0.0014570408587963 TotalHours : 0.0349689806111111 TotalMinutes : 2.09813883666667 TotalSeconds : 125.8883302
It should be no surprise that it’s much faster to return only the data that you want, because there’s so much less information that needs to be passed back from the CMDB. Also, during the first pipeline, the CPU utilization was quite high (ranging between 60-80%) where utilization was split between PowerShell (PowerShell does a lot of adaptation of the returned projection), the SQL server and the DataAccess Service.
Here are some of the filters that I tested against the System.WorkItem.Incident.ProjectionType projection:
'title -like "Ipsum*" -and CreatedByUser.DisplayName -like "D*"' 'title -like "Ipsum*" -and RelatedWorkItems.DisplayName -like "M*"' 'title -like "Ipsum*" -and RelatedWorkItems.DisplayName -eq "MA37"' 'RelatedWorkItems.DisplayName -eq "MA37"' 'priority = 2 -and RelatedWorkItems.DisplayName -eq "MA37"' 'priority -gt 1 -and RelatedWorkItems.DisplayName -eq "MA37"' 'priority -lt 3 -and RelatedWorkItems.DisplayName -eq "MA37"' 'priority -le 3 -and RelatedWorkItems.DisplayName -eq "MA37"' 'priority <= 3 -and RelatedWorkItems.DisplayName -eq "MA37"' 'priority -ne 3 -and RelatedWorkItems.DisplayName -eq "MA37"' 'priority -ne 3 -and RelatedWorkItems.DisplayName -notlike "MA3*"' 'priority -eq 3 -and Status -eq "Closed" -and RelatedWorkItems.DisplayName -notlike "MA3*"' 'priority -eq 3 -and AssignedUser.displayname -like "D*" -and Status -eq "Closed"
-and RelatedWorkItems.DisplayName -notlike "MA3*"'
Each time, the difference in time between client side and server side filtering is huge!
Here’s the script:
1: ### 2: ### filters have the form of:
3: ### [alias.]propertyname <operator> value 4: ### if there's no ".", then the assumption is that the
5: ### criteria is looking for the property of a seed 6: ### if there is a ".", then it's a property of a relationship
7: ### the relationship is described by the alias 8: ###
9: [CmdletBinding()] 10: param (
11: [parameter(Mandatory=$true,Position=0)] 12: $projection,
13: [parameter(Mandatory=$true,Position=1)][string]$filter, 14: [parameter()][switch]$results
15: ) 16:
17: # determine whether the property is an enumeration type 18: function Test-IsEnum
19: { 20: param ( $property )
21: if ( $property.SystemType.Name -eq "Enum" ) { return $true } 22: return $false
23: } 24: # Get the string which provides a reference in our criteria to the
25: # management pack which contains the element we're searching against 26: function Get-ReferenceString
27: { 28: param (
29: $ManagementPack, 30: [ref]$alias
31: ) 32: $alias.Value = $ManagementPack.Name.Replace(".","")
33: $refstring = '<Reference Id="{0}" PublicKeyToken="{1}" Version="{2}" Alias="{3}" />' 34: $refstring -f $ManagementPack.Name,$ManagementPack.KeyToken,$ManagementPack.Version,$Alias.Value
35: } 36:
37: # retrieve the property from the class 38: # we want to do this because we may get a property from the user which has the case
39: # incorrect, this allows us to match property names case insensitively 40: function Get-ClassProperty
41: { 42: param ( $Class, $propertyName )
43: $property = ($Class.GetProperties("Recursive")|?{$_.name -eq $propertyName}) 44: if ( ! $property ) { throw ("no such property '$propertyName' in " + $Class.Name) }
45: return $property 46: }
47: # in the case that the value that we got is applicable to an enum, look up the 48: # guid that is needed for the comparison and substitute that guid value
49: # replace the '*' with '%' which is needed by the criteria 50: function Get-ProperValue
51: { 52: param ( $Property, $value )
53: if ( Test-IsEnum $property ) 54: {
55: $value = get-scsmenumeration $property.type|?{$_.displayname -eq $value}|%{$_.id} 56: }
57: return $value -replace "\*","%" 58: }
59: # create the XML expression which describes the criteria 60: function Get-Expression
61: { 62: param (
63: $TypeProjection, 64: [Hashtable]$POV,
65: [ref]$neededReferences 66: )
67: $Property = $POV.Property 68: $Operator = $POV.Operator
69: $Value = $POV.Value 70: $ExpressionXML = @'
71: <Expression> 72: <SimpleExpression>
73: <ValueExpressionLeft><Property>{0}</Property></ValueExpressionLeft> 74: <Operator>{1}</Operator>
75: <ValueExpressionRight><Value>{2}</Value></ValueExpressionRight> 76: </SimpleExpression>
77: </Expression> 78: '@
79: [ref]$MPAlias = $null 80:
81: # a proper property reference in a projection criteria looks like this: 82: # <Property>
83: # $Context/Path[Relationship='CustomSystem_WorkItem_Library!System.WorkItemAffectedUser' 84: # TypeConstraint='CustomSystem_Library!System.User']/
85: # Property[Type='CustomSystem_Library!System.User']/FirstName$ 86: # </Property>
87: # we need to collect all the bits and do the same 88: # if the property has a "." in it, we will assume that this is the property
89: # of a relationship. Therefore, get the relationship and construct the 90: # appropriate string for the property access
91: # 92: # This routine only supports a single ".", anything more complicated and this will
93: # fail 94: if ( $property -match "\." )
95: { 96: $alias,$prop = $property -split "\."
97: $component = $projection.TypeProjection[$alias] 98: $references = @()
99: $NS = "Microsoft.EnterpriseManagement" 100: $ConfigNS = "${NS}.Configuration"
101: $ComponentType = "${ConfigNS}.ManagementPackTypeProjectionComponent" 102: if ( $component -isnot $ComponentType)
103: { 104: throw "'$alias' not found on projection"
105: } 106: $target = $component.TargetType
107: $references += Get-ReferenceString $target.GetManagementPack() $MPAlias 108: $TargetFQN = "{0}!{1}" -f $MPAlias.Value,$Target.Name
109: $property = Get-ClassProperty $target $prop 110: $value = Get-ProperValue $property $value
111: 112: $relationship = $component.Relationship
113: $references += Get-ReferenceString $relationship.GetManagementPack() $MPAlias 114: $relationshipFQN = "{0}!{1}" -f $MPAlias.Value,$relationship.name
115: 116: $PropString = '$Context/Path[Relationship=''{0}'' TypeConstraint=''{1}'']/Property[Type=''{1}'']/{2}$'
117: $XPATHSTR = $PropString -f $RelationshipFQN,$TargetFQN,$property.Name 118:
119: $Expression = $ExpressionXML -f $XPATHSTR,$QueryOperator,$value 120: $neededReferences.Value = $references | sort-object -uniq
121: return $Expression 122: }
123: else 124: {
125: $SeedClass = get-scsmclass -id $projection.TargetType.Id 126: $property = Get-ClassProperty $SeedClass $property
127: $value = Get-ProperValue $Property $value 128:
129: $SeedMP = $SeedClass.GetManagementPack() 130: $reference = Get-ReferenceString $SeedMP $MPAlias
131: $typeFQN = "{0}!{1}" -f $MPAlias.Value,$SeedClass.Name 132:
133: $PropString = '$Context/Property[Type=''{0}'']/{1}$' -f $typeFQN,$Property.Name 134: $Expression = $ExpressionXML -f $PropString,$Operator,$Value
135: $neededReferences.Value = $reference 136: return $Expression
137: } 138: }
139: 140: trap { $error[0];exit }
141: if ( $projection -is "psobject" ) 142: {
143: $projection = $projection.__base 144: }
145: $ProjectionType = "Microsoft.EnterpriseManagement.Configuration.ManagementPackTypeProjection" 146: if ( $projection -isnot $ProjectionType )
147: { 148: throw "$projection is not a projection and cannot be converted"
149: } 150: # right now, only AND is supported,
151: # eventually, OR will be supported 152: $GroupOperators = " -and "
153: # and the conversion to what is needed in the criteria 154: $OperatorConverter = @{
155: "=" = "Equal" 156: "-eq" = "Equal"
157: "!=" = "NotEqual" 158: "-ne" = "NotEqual"
159: "-like" = "Like" 160: "-notlike" = "NotLike"
161: "<" = "Less" 162: "-lt" = "Less"
163: ">" = "Greater" 164: "-gt" = "Greater"
165: ">=" = "GreaterEqual" 166: "-ge" = "GreaterEqual"
167: "<=" = "LessEqual" 168: "-le" = "LessEqual"
169: } 170: # a list of allowed operators, generated from the converter
171: $Operators = ($OperatorConverter.Keys |%{" $_ "}) -join "|" 172: # split the filter up based on the GroupOperator
173: $filters = @($filter.ToString() -split $GroupOperators | %{$_.trim()}) 174: # some variables that we will need
175: [ref]$neededrefs = $null 176: $Expressions = @()
177: $ReferenceStrings = @() 178: # loop through the filters and construct some XML which we will use
179: foreach ( $filterString in $filters) 180: {
181: # check to be sure we have a valid filter which includes 182: # a property, an operator and a value
183: $foundMatch = $filterString.toString() -match "(?<p>.*)(?<o>$operators)(?<v>.*)" 184: if ( ! $foundMatch )
185: { 186: throw "bad filter $filter"
187: } 188: # manipulate the found elements into a PropertyOperatorValue hashtable
189: # which we will use to encapsulate the filter 190: $Property = $matches['p'].Trim()
191: $Operator = $matches['o'].Trim() 192: $QueryOperator = $OperatorConverter[$Operator]
193: if ( ! $Operator ) { throw "Bad Operator '$Operator'" } 194: $Value = $matches['v'].Trim() -replace '"' -replace "'"
195: $POV = @{ 196: Property = $Property
197: Operator = $QueryOperator 198: Value = $Value
199: } 200: # now go get the expression that we need for the criteria
201: # pass the projection, the PropertyOperatorValue hashtable 202: # and the needed references (as a reference variable }
203: $expressions += get-expression $projection $POV $neededrefs 204: $neededRefs.Value | %{ $ReferenceStrings += $_ }
205: } 206: # now that we have looped through the filters, construct the XML
207: # which we need to call the ObjectProjectCriteria constructor 208: # start off with the start of the criteria XML
209: $CriteriaString = '<Criteria xmlns="http://Microsoft.EnterpriseManagement.Core.Criteria/">' 210: # now add the references that are needed in the criteria
211: $ReferenceStrings | sort -uniq | %{ $CriteriaString += "`n $_" } 212: # if we actually had multiple filters, add the
213: # <And> 214: if ( $Filters.count -gt 1 )
215: { 216: $CriteriaString += "`n<Expression>"
217: $CriteriaString += "`n <And>" 218: }
219: # now, for each of the expressions, add it to the criteria string 220: foreach($ex in $expressions ) { $CriteriaString += "`n $ex" }
221: # and in the case where we have filters that have and "-and", add the 222: # </And> to finish correctly
223: if ( $Filters.Count -gt 1) 224: {
225: $CriteriaString += "`n </And>" 226: $CriteriaString += "`n</Expression>"
227: } 228: $CriteriaString += "`n</Criteria>"
229: write-verbose $CriteriaString 230: # at this stage, the criteria XML should be complete, so we can create the
231: # criteria object 232: $CTYPE = "Microsoft.EnterpriseManagement.Common.ObjectProjectionCriteria"
233: 234: $criteriaobject = new-object $CTYPE $CriteriaString,$projection,$projection.ManagementGroup
235: if ( $criteriaObject -and $Results ) 236: {
237: get-scsmobjectprojection -criteria $criteriaobject 238: }
239: elseif ( $criteriaObject ) 240: {
241: $criteriaObject 242: }
243:
I added a Result parameter to the script which calls Get-SCSMObjectProjection, just for convenience. Eventually, I’ll add this logic into the filter parameter for the cmdlet, so it will be part of the cmdlet rather than this addition.
A tool for table formatting 3 comments
As I was working on the SMLets CodePlex project, I created a bunch of new cmdlets and I wanted to make sure that the output looked good and was useful. In order to do that I needed to sling a bunch of XML and create my own formatting file. If you’ve ever looked at the format files in PowerShell (to see what’s shipped, type get-childitem “$pshome/*.format.ps1xml”) it can be pretty daunting, so most folks (me included) start with something that works an then modify it. After I did that a few times, I reckoned that I had better build a tool to make life easier. My requirements were pretty straight-forward; build a tool that emitted the XML that I needed to put in the formatting file. Second, commit no unnatural acts with regard to parameters to create the XML. What I really wanted, is to use Format-Table to get the output exactly how I wanted and then just substitute my tool for Format-Table. Thus, New-TableFormat is born.
The following is how I use it, for this example, I’m just using a process object in this example, but the concept applies to any object. First I get the output looking exactly like I want, so in this case, I show the processID, the handles, the name and then the handles in KB. In order to do that last bit, I need to use a script block. After that, it’s simply replace format-table with new-tableformat and hey! presto! I’ve got my XML!
PS> get-process lsass|format-table id,handles,name,@{L="HandlesKB";E={$_.Handles/1KB};A="Right";F="{0:N2}"} -au Id Handles Name HandlesKB -- ------- ---- --------- 552 1274 lsass 1.24 PS> get-process lsass|new-tableformat id,handles,name,@{L="HandlesKB";E={$_.Handles/1KB};A="Right";F="{0:N2}"} -au <View> <Name>ProcessTable</Name> <ViewSelectedBy> <TypeName>System.Diagnostics.Process</TypeName> </ViewSelectedBy> <TableControl> <AutoSize /> <TableHeaders> <TableColumnHeader><Label>id</Label></TableColumnHeader> <TableColumnHeader><Label>handles</Label></TableColumnHeader> <TableColumnHeader><Label>name</Label></TableColumnHeader> <TableColumnHeader> <Label>HandlesKB</Label> <Alignment>Right</Alignment> </TableColumnHeader> </TableHeaders> <TableRowEntries> <TableRowEntry> <TableColumnItems> <TableColumnItem><PropertyName>id</PropertyName></TableColumnItem> <TableColumnItem><PropertyName>handles</PropertyName></TableColumnItem> <TableColumnItem><PropertyName>name</PropertyName></TableColumnItem> <TableColumnItem><ScriptBlock>$_.Handles/1KB</ScriptBlock><FormatString>{0:N2}</FormatString></TableColumnItem> </TableColumnItems> </TableRowEntry> </TableRowEntries> </TableControl> </View>
In order for me to take advantage of this, I need to save it to a file and then call update-formatdata on the new file. You might notice that this is not quite complete as it doesn’t have the required elements for the beginning and end of the file, so I’ve got another parameter –complete which emits complete, standalone XML which I can dump into a file and then import. Once imported I can just use format-table as usual (in this case since there’s already a table format for process objects, I need to include the view name, which is “ProcessTable”, in this case. If you’re building formatting for an object of your own creation or doesn’t already exist, this last step is not necessary. Just output the object and the formatter takes care of everything automatically.
PS> get-process lsass|new-tableformat id,handles,name,@{L="HandlesKB";E={$_.Handles/1KB};A="Right";F="{0:N2}"} -au -comp > handleKB.format.ps1xml PS> update-formatdata handleKB.format.ps1xml PS> get-process lsass | format-table -view ProcessTable id handles name HandlesKB -- ------- ---- --------- 552 1263 lsass 1.23
Here’s the script, it’s only 125 lines or so!
param ( [Parameter(Mandatory=$true,ValueFromPipeline=$true)]$object, [Parameter(Mandatory=$true,Position=0)][object[]]$property, [Parameter()][switch]$Complete, [Parameter()][switch]$auto, [Parameter()][string]$name, [Parameter()][switch]$force ) End { if ( $object ) { if ( $object -is "PSObject") { $TN = $object.psobject.typenames[0] } else { $TN = $object.gettype().fullname } } elseif ( $name ) { $TN = $name } $NAME = $TN.split(".")[-1] $sb = new-object System.Text.StringBuilder if ( $complete ) { [void]$sb.Append("<Configuration>`n") [void]$sb.Append(" <ViewDefinitions>`n") } [void]$sb.Append(" <View>`n") [void]$sb.Append(" <Name>${Name}Table</Name>`n") [void]$sb.Append(" <ViewSelectedBy>`n") [void]$sb.Append(" <TypeName>${TN}</TypeName>`n") [void]$sb.Append(" </ViewSelectedBy>`n") [void]$sb.Append(" <TableControl>`n") if ( $auto ) { [void]$sb.Append(" <AutoSize />`n") } [void]$sb.Append(" <TableHeaders>`n") # # Now loop through the properties, creating a header for each # provided property # foreach($p in $property) { if ( $p -is "string" ) { [void]$sb.Append(" <TableColumnHeader><Label>${p}</Label></TableColumnHeader>`n") } elseif ( $p -is "hashtable" ) { $Label = $p.keys | ?{$_ -match "^L|^N" } if ( ! $Label ) { throw "need Name or Label Key" } [void]$sb.Append(" <TableColumnHeader>`n") [void]$sb.Append(" <Label>" + $p.$label + "</Label>`n") $Width = $p.Keys |?{$_ -match "^W"}|select -first 1 if ( $Width ) { [void]$sb.Append(" <Width>" + $p.$Width + "</Width>`n") } $Align = $p.Keys |?{$_ -match "^A"}|select -first 1 if ( $Align ) { [void]$sb.Append(" <Alignment>" + $p.$align + "</Alignment>`n") } [void]$sb.Append(" </TableColumnHeader>`n") # write-host -for red ("skipping " + $p.Name + " for now") } } [void]$sb.Append(" </TableHeaders>`n") [void]$sb.Append(" <TableRowEntries>`n") [void]$sb.Append(" <TableRowEntry>`n") [void]$sb.Append(" <TableColumnItems>`n") foreach($p in $property) { if ( $p -is "string" ) { [void]$sb.Append(" <TableColumnItem><PropertyName>${p}</PropertyName></TableColumnItem>`n") } elseif ( $p -is "hashtable" ) { [void]$sb.Append(" <TableColumnItem>") $Name = $p.Keys | ?{ $_ -match "^N" }|select -first 1 if ( $Name ) { $v = $p.$Name [void]$sb.Append("<PropertyName>$v</PropertyName>") } $Expression = $p.Keys | ?{ $_ -match "^E" }|select -first 1 if ( $Expression ) { $v = $p.$Expression [void]$sb.Append("<ScriptBlock>$v</ScriptBlock>") } $Format = $p.Keys | ?{$_ -match "^F" }|select -first 1 if ( $Format ) { $v = $p.$Format [void]$sb.Append("<FormatString>$v</FormatString>") } [void]$Sb.Append("</TableColumnItem>`n") } } [void]$sb.Append(" </TableColumnItems>`n") [void]$sb.Append(" </TableRowEntry>`n") [void]$sb.Append(" </TableRowEntries>`n") [void]$sb.Append(" </TableControl>`n") [void]$sb.Append(" </View>`n") if ( $complete ) { [void]$sb.Append(" </ViewDefinitions>`n") [void]$sb.Append("</Configuration>`n") } $sb.ToString() }
the thing to note here is that I’m just building the XML string and then emitting it, I’m not using all the XML programming goo, which would probably be better practice, but I didn’t want to be bothered (clearly, that could be a future enhancement). You’ll notice that it handles pretty much everything that format-table does.
I hope this is as useful for you as it is for me
missing in action 1 comment
Clearly i haven’t posted for a long while, and while there are many reasons which I don’t need to go into, it’s not because I’ve been slacking. Most recently, I’ve been working on a codeplex project (http://smlets.codeplex.com) which uses PowerShell to interact with Service Manager (i’ve been working on Service Manager for a while). The project has been great fun and has let me do a quite a bit of cmdlet development, which I really enjoy. If you use Service Manager, you should definitely check it out.
The real point of this post is to share my experience over the last couple of days at the PowerShell Deep Dive which happened at the Experts Conference in Las Vegas. I have always been extremely proud of my work while I was working on PowerShell; giving birth to that product was a long, and at times, difficult experience which taught me a lot about myself. However, seeing how it has affected (in a positive way) so many people really makes all that worth while. Seeing the excitement in others cannot help but uplift the spirit and I am so glad that I had the opportunity to contribute. The stories about how PowerShell have improved the lives of others in concrete ways is awesome.
The experience has reinvigorated me as well; talking to so many intelligent, articulate people has given me a whole bunch of new ideas to write on, so I hope you will see a stream of posts here (as long as I can follow through!). My next post will be on creating custom formatting, hopefully posted before the plane lands.
Service Manager Announcements Leave a comment
I’ve written in previous blogs on how to get data out of Service Manager, and generally, that data is usually simple text, numbers or sometimes an enumeration (which is pretty easy to convert to text). However, Service Manager also allows you to store text with formatting (rich text data) which can be pretty difficult to view. First, let’s create an announcement:
We can use one of the scripts created in an earlier posting to retrieve an instance of the announcement:
PS> get-smclass announcement.item|get-scsmobject|fl
Id : 2
Title : Announcement 001
Body : {\rtf1\ansi\ansicpg1252\uc1\htmautsp\deff2{\fonttbl{\f0\fcharset0
Times New Roman;}{\f2\fcharset0 Sego
e UI;}{\f3\fcharset0 Calibri;}{\f4\fcharset0 Copperplate Gothic
Bold;}}{\colortbl\red0\green0\blue0;\r
ed255\green255\blue255;\red255\green0\blue0;}
{\*\listtable
{\list\listtemplateid1\listhybrid
. . .
b0\jclisttab\tx720\fi-360\ql\par}
}
}
ExpirationDate : 6/28/2010 7:00:00 AM
Priority : System.Announcement.PriorityEnum.Medium
DisplayName : Announcement 001
Type : System.Announcement.Item
Name : 2
Path :
FullName : System.Announcement.Item:2
Since the body property of the announcement is rich text, it really isn’t readable in this format, but we can fix that with a fun little script. This is one of those scripts that allow us to mix the command line and the graphical environment. We’ll call this script Display-RichText.ps1 since that’s what it does!
|
001
002 003 004 005 006 007 008 009 010 011 012 013 014 015 016 017 018 019 020 021 022 023 024 025 026 027 028 029 030 031 032 033 034 035 036 037 038 039 040 041 042 043 044 045 046 047 048 049 050 051 052 |
param (
$string ) begin { [void][reflection.assembly]::LoadWithPartialName(“System.Windows.Forms”) [void][reflection.assembly]::LoadWithPartialName(“System.Drawing”) ## the form $form = new-object System.Windows.Forms.Form $form.size = new-object System.Drawing.Size 400,400 ## the Rich text box ## Quit button $form.controls.add($text) } |
Now let’s see what we can do!
PS> $announcement = get-smclass announcement.item|get-scsmobject PS> display-richtext $announcement.body
Now we can see the contents of the announcement!
Saving Service Manager Instances in a CSV file Leave a comment
In a post I did a few months ago, I wrote how it you can retrieve data from Service Manager via a PowerShell script, but I didn’t show much more than just plain output. Earlier this week I was asked if it would be possible to export configuration items in a CSV file. By using the script I wrote months ago, we have all the tools we need. Let’s review the previous script:
|
001
002 003 004 005 006 007 008 009 010 011 012 013 014 015 016 017 018 019 020 021 022 023 024 025 026 027 028 029 030 031 032 033 034 |
param ( $classname )
$emg = new-object microsoft.enterprisemanagement.enterprisemanagementgroup localhost $class = $emg.EntityTypes.GetClasses()|?{$_.name -eq $classname} if ( ! $class ) { Write-Error "`nERROR: Class ‘$classname’ not found, exiting." exit } $DEFAULT = [Microsoft.EnterpriseManagement.Common.ObjectQueryOptions]::Default $EMOT = [Microsoft.EnterpriseManagement.Common.EnterpriseManagementObject] # Retrieve the interface for EntityObjects, which we’ll use when we create our generic method $IMGMT = $emg.EntityObjects.GetType() # the types of the parameters, this is so we can find the right method [type[]]$TYPES = [Microsoft.EnterpriseManagement.Configuration.ManagementPackClass], [Microsoft.EnterpriseManagement.Common.ObjectQueryOptions] # Retrieve the method $ObjectReader = $IMGMT.GetMethod("GetObjectReader",$TYPES) # Create a generic method $GenericMethod = $ObjectReader.MakeGenericMethod($EMOT) # Invoke the method with our arguments [array]$arguments = [Microsoft.EnterpriseManagement.Configuration.ManagementPackClass]$class,$DEFAULT $GenericMethod.invoke($emg.EntityObjects,$arguments) | %{ # Create a custom object based on the original object $o = new-object psobject $_ # elevate the properties in the Values collection to the top level $o.values|%{ $o | add-member -force NoteProperty $_.Type $_.Value } # assign a synthetic typename to the object, so we can use our formatting # more easily $name = $_.GetLeastDerivedNonAbstractClass().name $o.psobject.typenames.Insert(0, "EnterpriseManagementObject#$name") # now, emit the object! $o } |
We’ll call this script get-object (and it assumes that you’ve already loaded the Microsoft.EnterpriseManagement.Core.dll), and just to remind you, here’s how it works:
PS> get-object microsoft.windows.computer|format-table DisplayName,Netbios*,PrincipalName -au DisplayName NetbiosComputerName NetbiosDomainName PrincipalName ----------- ------------------- ----------------- ------------- JWT-SCDW$ JWT-SCDW WOODGROVE JWT-SCDW.woodgrove.com WIN-752HJBSX24M.woodgrove.com WIN-752HJBSX24M WOODGROVE WIN-752HJBSX24M.woodgrove.com
You provide a class name to it and it retrieves all the instances of that class. Now, to save these instances in a CSV file, I just need to use the Export-CSV cmdlet that is part of the PowerShell distribution and viola!
PS> get-object microsoft.windows.computer|Export-CSV Microsoft.Windows.Computer.csv PS> get-content .\Microsoft.Windows.Computer.csv #TYPE EnterpriseManagementObject#Microsoft.Windows.Computer "PrincipalName","DNSName","NetbiosComputerName","NetbiosDomainName","IPAddres... "JWT-SCDW.woodgrove.com","JWT-SCDW.woodgrove.com","JWT-SCDW","WOODGROVE",,,"S... "WIN-752HJBSX24M.woodgrove.com","WIN-752HJBSX24M.woodgrove.com","WIN-752HJBSX...
You can use this to create a copy of your data, or use it as a way to exchange your data with other applications. Because of the way that other applications use CSV files, you may need to remove the first line of the CSV file which describes what the object was from the PowerShell perspective, but otherwise, you should be able to use the CSV file easily!
More and more with management packs Leave a comment
I’ve been a little slow in updating here, but that doesn’t reflect any inactivity, just my poor rhythm for posting.
Earlier in the year I did a post on what was possible to do in a single line of PowerShell. In this post, I’m going to take that further and explore this in more detail. Specifically, we’ll take a closer look at all of the possibilities with PowerShell and Service Manager management packs. I’ve blogged on management packs in the past, but generally on what to do with them rather than what they are. In this blog, we’ll take a look at the various properties of a management pack and how easy they are to access from PowerShell.
First, if we take a look at the available properties of a management pack, we see a pretty rich object model.
PS> $emg = new-object Microsoft.EnterpriseManagement.EnterpriseManagementGroup Localhost
PS> $emg.ManagementPacks.GetManagementPacks()|get-member
TypeName: Microsoft.EnterpriseManagement.Configuration.ManagementPack
Name MemberType Definition
---- ---------- ----------
AcceptChanges Method System.Void AcceptChanges(), System.Void AcceptChanges(Micros...
AddService Method System.Void AddService[T, V](string name, Microsoft.Enterpris...
CheckVersionCompatibility Method System.Void CheckVersionCompatibility(Microsoft.EnterpriseMan...
Configure Method System.Void Configure(System.IO.Stream configuration)
DeleteEnterpriseManagementObjectGroup Method System.Void DeleteEnterpriseManagementObjectGroup(Microsoft.E...
DeleteMonitoringObjectGroup Method System.Void DeleteMonitoringObjectGroup(Microsoft.EnterpriseM...
Dispose Method System.Void Dispose()
Equals Method bool Equals(System.Object obj)
FindManagementPackElementByName Method Microsoft.EnterpriseManagement.Configuration.ManagementPackEl...
GetCategories Method Microsoft.EnterpriseManagement.Configuration.ManagementPackEl...
. . .
RemoveService Method System.Void RemoveService(string name)
RemoveServices Method System.Void RemoveServices()
ToString Method string ToString()
Verify Method System.Void Verify()
ContentReadable Property System.Boolean ContentReadable {get;}
DefaultLanguageCode Property System.String DefaultLanguageCode {get;set;}
Description Property System.String Description {get;set;}
DisplayName Property System.String DisplayName {get;set;}
. . .
SchemaVersion Property System.Version SchemaVersion {get;}
Sealed Property System.Boolean Sealed {get;}
TimeCreated Property System.DateTime TimeCreated {get;}
Version Property System.Version Version {get;set;}
VersionId Property System.Guid VersionId {get;}
In fact, you’ll see about 150 different methods and properties. I’ve discussed various aspects of some of the properties and methods in earlier posts, but I want to concentrate on the methods in this posting, and the methods I want to look at specifically are the Get* methods. These methods return information about the contents and configuration of the management packs. Since there are about 100 Get* methods, i’d like to reduce the list to something more manageable, so we’ll only look at the get* methods that don’t take any arguments.
PS> $emg.ManagementPacks.GetManagementPacks()|get-member get*|?{$_.definition -match "\(\)"}
TypeName: Microsoft.EnterpriseManagement.Configuration.ManagementPack
Name MemberType Definition
---- ---------- ----------
GetCategories Method Microsoft.EnterpriseManagement.Configuration.ManagementPackElementCollection[...
GetClasses Method Microsoft.EnterpriseManagement.Configuration.ManagementPackElementCollection[...
GetConfigurationGroups Method Microsoft.EnterpriseManagement.Configuration.ManagementPackElementCollection[...
GetConsoleTasks Method Microsoft.EnterpriseManagement.Configuration.ManagementPackElementCollection[...
GetDataTypes Method Microsoft.EnterpriseManagement.Configuration.ManagementPackElementCollection[...
GetDataWarehouseDataSets Method Microsoft.EnterpriseManagement.Configuration.ManagementPackElementCollection[...
GetDataWarehouseScripts Method Microsoft.EnterpriseManagement.Configuration.ManagementPackElementCollection[...
GetDiagnostics Method Microsoft.EnterpriseManagement.Configuration.ManagementPackElementCollection[...
GetDimensionTypes Method Microsoft.EnterpriseManagement.Configuration.ManagementPackElementCollection[...
GetDiscoveries Method Microsoft.EnterpriseManagement.Configuration.ManagementPackElementCollection[...
GetEnumerations Method Microsoft.EnterpriseManagement.Configuration.ManagementPackElementCollection[...
GetFactTypes Method Microsoft.EnterpriseManagement.Configuration.ManagementPackElementCollection[...
GetFolderItems Method Microsoft.EnterpriseManagement.Configuration.ManagementPackItemCollection[Mic...
GetFolders Method Microsoft.EnterpriseManagement.Configuration.ManagementPackElementCollection[...
GetForms Method Microsoft.EnterpriseManagement.Configuration.ManagementPackElementCollection[...
GetHashCode Method int GetHashCode()
GetImageReferences Method Microsoft.EnterpriseManagement.Configuration.ManagementPackItemCollection[Mic...
GetImages Method Microsoft.EnterpriseManagement.Configuration.ManagementPackElementCollection[...
GetLanguagePacks Method Microsoft.EnterpriseManagement.Configuration.ManagementPackElementCollection[...
GetLinkedReports Method Microsoft.EnterpriseManagement.Configuration.ManagementPackElementCollection[...
GetManagementPackCategories Method System.Collections.Generic.IList[Microsoft.EnterpriseManagement.Configuration...
GetMeasureTypes Method Microsoft.EnterpriseManagement.Configuration.ManagementPackElementCollection[...
GetModuleTypes Method Microsoft.EnterpriseManagement.Configuration.ManagementPackElementCollection[...
GetMonitors Method Microsoft.EnterpriseManagement.Configuration.ManagementPackElementCollection[...
GetObjectTemplates Method Microsoft.EnterpriseManagement.Configuration.ManagementPackElementCollection[...
GetOutriggerTypes Method Microsoft.EnterpriseManagement.Configuration.ManagementPackElementCollection[...
GetOverrides Method Microsoft.EnterpriseManagement.Configuration.ManagementPackElementCollection[...
GetRecoveries Method Microsoft.EnterpriseManagement.Configuration.ManagementPackElementCollection[...
GetRelationshipFactTypes Method Microsoft.EnterpriseManagement.Configuration.ManagementPackElementCollection[...
GetRelationships Method Microsoft.EnterpriseManagement.Configuration.ManagementPackElementCollection[...
GetReportParameterControls Method Microsoft.EnterpriseManagement.Configuration.ManagementPackElementCollection[...
GetReportResources Method Microsoft.EnterpriseManagement.Configuration.ManagementPackElementCollection[...
GetReports Method Microsoft.EnterpriseManagement.Configuration.ManagementPackElementCollection[...
GetResources Method Microsoft.EnterpriseManagement.Configuration.ManagementPackElementCollection[...
GetRules Method Microsoft.EnterpriseManagement.Configuration.ManagementPackElementCollection[...
GetSchemaTypes Method Microsoft.EnterpriseManagement.Configuration.ManagementPackElementCollection[...
GetSecureReferences Method Microsoft.EnterpriseManagement.Configuration.ManagementPackElementCollection[...
GetServiceLevelObjectives Method Microsoft.EnterpriseManagement.Configuration.ManagementPackElementCollection[...
GetServices Method System.Collections.Generic.IList[T] GetServices[T]()
GetStringResources Method Microsoft.EnterpriseManagement.Configuration.ManagementPackElementCollection[...
GetTasks Method Microsoft.EnterpriseManagement.Configuration.ManagementPackElementCollection[...
GetTemplates Method Microsoft.EnterpriseManagement.Configuration.ManagementPackElementCollection[...
GetType Method type GetType()
GetTypeProjections Method Microsoft.EnterpriseManagement.Configuration.ManagementPackElementCollection[...
GetUIPages Method Microsoft.EnterpriseManagement.Configuration.ManagementPackElementCollection[...
GetUIPageSets Method Microsoft.EnterpriseManagement.Configuration.ManagementPackElementCollection[...
GetUnitMonitorTypes Method Microsoft.EnterpriseManagement.Configuration.ManagementPackElementCollection[...
GetViews Method Microsoft.EnterpriseManagement.Configuration.ManagementPackElementCollection[...
GetViewTypes Method Microsoft.EnterpriseManagement.Configuration.ManagementPackElementCollection[...
GetWarehouseModuleTypes Method Microsoft.EnterpriseManagement.Configuration.ManagementPackElementCollection[...
There are still 50 of these, but there are some methods that are easier to use. First nearly all of these methods return a ManagementPackElementCollection, only 2 return a ManagementPackItemCollection (GetFolderItems and GetImageReferences), and 4 others require a typed invocation (GetServices, GetManagementPackCategories, GetResources and GetServiceLevelObjectives). We'll focus on the methods that return a ManagementPackElementCollection and that don't take a generic type, because they're the simplest to deal with. Since these methods don't take any arguments, we can just call them and see what we get back. The method name should give us a clue about what we'll get back, and you can check the Service Manager SDK documentation as well for more information.
As a first example, we'll retrieve the System.Library management pack and inspect all the classes declared in the mp and get their base class.
PS> $emg.ManagementPacks.GetManagementPacks()|?{$_.Name -eq "System.Library"}|
>> %{$_.GetClasses()}|ft Abstract,Name,@{Label="Base";e={$emg.entitytypes.GetClass($_.base.id).Name}} -au
>>
Abstract Name Base
-------- ---- ----
True System.Entity
True System.Collections System.Entity
True System.ConfigItem System.Entity
True System.LogicalEntity System.ConfigItem
True System.ApplicationComponent System.LogicalEntity
True System.ComputerRole System.LogicalEntity
True System.Database System.ApplicationComponent
True System.Device System.LogicalEntity
True System.Computer System.Device
True System.FTPSite System.ApplicationComponent
True System.Group System.LogicalEntity
True System.LocalApplication System.LogicalEntity
True System.LogicalHardware System.LogicalEntity
True System.NetworkDevice System.Device
True System.OperatingSystem System.LogicalEntity
True System.Perspective System.LogicalEntity
True System.PhysicalEntity System.ConfigItem
True System.Printer System.Device
True System.Service System.LogicalEntity
True System.SoftwareInstallation System.LogicalEntity
True System.User System.LogicalEntity
False System.Domain.User System.User
True System.WebSite System.ApplicationComponent
Note that I'm using the EMG in the formatting directives to get the base class.
If we wanted to get the XML for one of these classes, we can do that with the CreateNavigator method. We'll collect the classes and then use the CreateNavigator method to get at the XML. This examples retrieves the System.Library management pack and then displays the XML for the System.ConfigItem class.
PS> $emg.ManagementPacks.GetManagementPacks()|?{$_.Name -eq "System.Library"}|
>> %{$_.GetClasses()}|?{$_.Name -eq "System.ConfigItem"}|%{$_.CreateNavigator().OuterXML}
>>
<ClassType ID="System.ConfigItem" Accessibility="Public" Abstract="true"
Base="System.Entity" Hosted="false" Singleton="false" Extension="false">
<Property ID="ObjectStatus" Type="enum" AutoIncrement="false" Key="false" CaseSensitive="false"
MaxLength="256" MinLength="0" Required="false" EnumType="System.ConfigItem.ObjectStatusEnum"
DefaultValue="System.ConfigItem.ObjectStatusEnum.Active" />
<Property ID="AssetStatus" Type="enum" AutoIncrement="false" Key="false" CaseSensitive="false"
MaxLength="256" MinLength="0" Required="false" EnumType="System.ConfigItem.AssetStatusEnum" />
<Property ID="Notes" Type="richtext" AutoIncrement="false" Key="false" CaseSensitive="false"
MaxLength="4000" MinLength="0" Required="false" />
</ClassType>
Pretty cool! The following example retrieves all the views in the system:
PS> $emg.ManagementPacks.GetManagementPacks()|%{$_.GetViews()}|ft name,description
Name Description
---- -----------
Microsoft.EnterpriseManagement.ServiceManager.UI.Adminis... Lists all subscriptions
Microsoft.EnterpriseManagement.ServiceManager.UI.Adminis... Lists all Run As accounts
Microsoft.EnterpriseManagement.ServiceManager.UI.Adminis... Contains general, portal, and solution settings
Microsoft.EnterpriseManagement.ServiceManager.UI.Adminis... Administration Overview
Microsoft.EnterpriseManagement.ServiceManager.UI.Adminis... Lists all templates
. . .
AllComputersView Lists all computers
AllPrintersView Lists all printers
QueuesView Lists all the queues available
TemplatesView Lists all the templates available
TasksView Lists all the console tasks defined in the system
EnumerationView Displays all the lists available
GroupsView Lists all groups
WorkItemExclusionRule Work item exclusion workflow view
ChangeManagement.Views.ChangeRequestsCancelled Lists all canceled change requests
ChangeManagement.Views.ChangeRequestsCompleted Lists all completed change requests
ChangeManagement.Views.ChangeRequestsClosed Lists all closed change requests
ChangeManagement.Views.AllChangeRequests Lists all change requests
ChangeManagement.Views.ChangeRequestsRejected Lists all rejected change requests
ChangeManagement.Views.ChangeRequestsInReview Change Requests: In Review
. . .
System.WorkItem.Incident.Pending.View Lists all pending incidents
System.WorkItem.Incident.OverDue.View Lists all overdue incidents
System.WorkItem.Incident.Active.Unassigned.View Lists all open unassigned incidents
Microsoft.SystemCenter.AllActiveAnnouncementsView Active Announcements
Microsoft.SystemCenter.AllAnnouncementsView All Announcements
And we can inspect the XML for the one of the views (say the AllPrintersView), with this one-liner:
PS> $emg.ManagementPacks.GetManagementPacks()|%{$_.GetViews()}|
>> ?{$_.name -eq "allprintersview"}| %{$_.createnavigator().outerxml}
<View ID="AllPrintersView" Accessibility="Public" Enabled="true" Target="Windows!Microsoft.AD.Printer" TypeID="SMConsole!GridViewType" Visible="true">
<Category>NotUsed</Category>
<Data>
<Adapters>
<Adapter AdapterName="dataportal:EnterpriseManagementObjectProjectionAdapter">
<AdapterAssembly>Microsoft.EnterpriseManagement.UI.SdkDataAccess</AdapterAssembly>
<AdapterType>Microsoft.EnterpriseManagement.UI.SdkDataAccess.DataAdapters.EnterpriseManagementObjectProjectionAdapter</AdapterType>
</Adapter>
<Adapter AdapterName="viewframework://Adapters/AdvancedList">
<AdapterAssembly>Microsoft.EnterpriseManagement.UI.ViewFramework</AdapterAssembly>
<AdapterType>Microsoft.EnterpriseManagement.UI.ViewFramework.AdvancedListSupportAdapter</AdapterType>
</Adapter>
<Adapter AdapterName="omsdk://Adapters/Criteria">
<AdapterAssembly>Microsoft.EnterpriseManagement.UI.SdkDataAccess</AdapterAssembly>
<AdapterType>Microsoft.EnterpriseManagement.UI.SdkDataAccess.DataAdapters.SdkCriteriaAdapter</AdapterType>
</Adapter>
</Adapters>
<ItemsSource>
<AdvancedListSupportClass DataTypeName="" AdapterName="viewframework://Adapters/AdvancedList" FullUpdateAdapter="dataportal:EnterpriseManagementObjectProjectionAdapter" DataSource="mom:ManagementGroup" IsRecurring="True" RecurrenceFrequency="{x:Static s:Int32.MaxValue}" FullUpdateFrequency="1" Streaming="true" xmlns="clr-namespace:Microsoft.EnterpriseManagement.UI.ViewFramework;assembly=Microsoft.EnterpriseManagement.UI.ViewFramework" xmlns:av="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib">
<AdvancedListSupportClass.Parameters>
<QueryParameter Parameter="TypeProjectionName" Value="Microsoft.Windows.PrinterView.ProjectionType" />
</AdvancedListSupportClass.Parameters>
</AdvancedListSupportClass>
</ItemsSource>
<Criteria>
<QueryCriteria Adapter="omsdk://Adapters/Criteria" xmlns="http://tempuri.org/Criteria.xsd">
<Criteria>
<FreeformCriteria>
<Freeform>
<Criteria xmlns="http://Microsoft.EnterpriseManagement.Core.Criteria/">
<Expression>
<SimpleExpression>
<ValueExpressionLeft>
<Property>$Context/Property[Type='System!System.ConfigItem']/ObjectStatus$</Property>
</ValueExpressionLeft>
<Operator>NotEqual</Operator>
<ValueExpressionRight>
<Value>$MPElement[Name="System!System.ConfigItem.ObjectStatusEnum.PendingDelete"]$</Value>
</ValueExpressionRight>
</SimpleExpression>
</Expression>
</Criteria>
</Freeform>
</FreeformCriteria>
</Criteria>
</QueryCriteria>
</Criteria>
</Data>
<Presentation>
<Columns>
<mux:ColumnCollection xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:mux="http://schemas.microsoft.com/SystemCenter/Common/UI/Views/GridView" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:datebinding="clr-namespace:Microsoft.EnterpriseManagement.UI.SdkDataAccess.Common;assembly=Microsoft.EnterpriseManagement.UI.SdkDataAccess">
<mux:Column Name="uncName" DisplayMemberBinding="{Binding Path=UNCName}" Width="120" DisplayName="Header_UNCName" Property="UNCName" DataType="s:String" />
<mux:Column Name="printerName" DisplayMemberBinding="{Binding Path=PrinterName}" Width="120" DisplayName="Header_PrinterName" Property="PrinterName" DataType="s:String" />
<mux:Column Name="description" DisplayMemberBinding="{Binding Path=Description}" Width="120" DisplayName="Header_Description" Property="Description" DataType="s:String" />
<mux:Column Name="location" DisplayMemberBinding="{Binding Path=Location}" Width="120" DisplayName="Header_Location" Property="Location" DataType="s:String" />
</mux:ColumnCollection>
</Columns>
<ViewStrings>
<ViewString ID="Header_UNCName">$MPElement[Name="AllPrintersView.Header_UNCName"]$</ViewString>
<ViewString ID="Header_PrinterName">$MPElement[Name="AllPrintersView.Header_PrinterName"]$</ViewString>
<ViewString ID="Header_Description">$MPElement[Name="AllPrintersView.Header_Description"]$</ViewString>
<ViewString ID="Header_Location">$MPElement[Name="AllPrintersView.Header_Location"]$</ViewString>
</ViewStrings>
</Presentation>
</View>
Since the CreateNavigator method is available on ManagementPackElement, we can use that method with every one of the objects returned by the methods mentioned above – it’s a great way to explore and see what’s going on in your management pack.
As an interesting aside, we can invoke all of these simple methods with a couple of lines of script. This will tell us the total number of each of the management pack elements returned by the method. The first line collects the names of the methods that we want to invoke. The second line creates a hash table to hold our results and the last line retrieves all the management packs, and invokes each method on the management pack object and adds the count of those elements to the hash table. This is a pretty cool trick to invoke a method without know what the method name is before hand.
PS> $names = $emg.ManagementPacks.GetManagementPacks()|
>> get-member get*s|
>> ?{$_.definition -match "ManagementPackElementCollection" -and
>> $_.definition -notmatch "\[T\]"}|%{$_.name}
PS> $counthash = $names | %{ $h = @{}}{$h.$_ = 0 } {$h }
PS> $emg.ManagementPacks.GetManagementPacks()|%{
>> $mp = $_
>> $names | %{ $counthash.$_ += $mp.$_.invoke().count }
>> }
>>
PS> $counthash
Name Value
---- -----
GetReportResources 28
GetModuleTypes 293
GetObjectTemplates 24
GetUIPages 48
GetOutriggerTypes 30
GetRules 53
GetTasks 6
GetReports 23
GetSchemaTypes 28
GetClasses 272
GetMonitors 47
GetTypeProjections 56
GetReportParameterControls 0
GetConfigurationGroups 0
GetDimensionTypes 26
GetRelationships 124
GetViewTypes 24
GetLinkedReports 0
GetStringResources 1211
GetFactTypes 4
GetDataTypes 45
GetTemplates 3
GetForms 16
GetFolders 85
GetWarehouseModuleTypes 9
GetMeasureTypes 2
GetSecureReferences 11
GetRecoveries 1
GetViews 138
GetDiscoveries 31
GetUIPageSets 225
GetEnumerations 460
GetDataWarehouseScripts 38
GetConsoleTasks 160
GetCategories 583
GetDataWarehouseDataSets 0
GetOverrides 49
GetImages 254
GetDiagnostics 0
GetLanguagePacks 89
GetRelationshipFactTypes 28
GetUnitMonitorTypes 239
woo hoo!

