Archive for July 2009

Getting system uptime   Leave a comment

Sometimes I miss my Unix system. Actually, that’s not true, sometimes I miss the tools that are available.  For example, I needed to figure out when the system booted.  Thankfully, there are performance counters that can tell me!

PS# cat get-uptime.ps1
$PCounter = "System.Diagnostics.PerformanceCounter"
$counter = new-object $PCounter System,"System Up Time"
$value = $counter.NextValue()
$uptime = [System.TimeSpan]::FromSeconds($counter.NextValue())
"Uptime: $uptime"
"System Boot: " + ((get-date) - $uptime)

Simple!

Posted July 28, 2009 by jtruher3 in PowerShell

PowerShell and the Service Manager Instance Space   Leave a comment

In my last blog, I wrote about the Service Manager Type Environment, this time we’re going to work with what’s called the instance space.  This “space” represents the actual data which is kept for your environment.  When you get data from one of the connectors, create a change request or incident, an instance (or a set of instances) is created.  In this post, we’ll investigate how we can take advantage of this from PowerShell.

FAREWELL TO SCRIPTING

This is also the time where we will bid adieu to a completely script based interaction model.  This is due to a couple of reasons; First and foremost, when you are working with the instance space in Service Manager, you need to use the EntityObjects interface on the EnterpriseManagementGroup object.  The methods that allow you to retrieve data from SM requires the use of generics, and PowerShell doesn’t have support in its syntax for direct method invocation which includes generics.

Let’s take a look:

PS> $emg.EntityObjects|gm getobjects|ft name,definition -au

Name                         Definition
----                         ----------
GetObjects                   System.Collections.Generic.IList[T] GetObjects[T](Microsoft.Enterpr...

We can see that PowerShell tells us that the methods require a type – you can see the bit I highlighted in red above, PowerShell indicates the type by putting it in square brackets, but sadly, you can’t call the method in this way.  There’s just no good way to call this method from within a PowerShell script, you have to write some C#.  PowerShell V2 will let me embed C# in a script and create a type (see Get-PowerShell » Embedded C# for a good example) on the fly which I could use to paint over the generics bit, but I think for my purposes, creating a compiled C# cmdlet will be better overall.

I need to do the following:

  1. Create a connection to the Data Access Server (create my EnterpriseManagementGroup object)
  2. Call the appropriate method on the EntityObjects Interface

In looking at the available methods, I see a number that might be useful

PS> $emg = new-object Microsoft.EnterpriseManagement.EnterpriseManagementGroup localhost
PS> $emg.EntityObjects|gm


   TypeName: Microsoft.EnterpriseManagement.InstancesManagement

Name                                MemberType Definition
----                                ---------- ----------
Equals                              Method     bool Equals(System.Object obj)
GetHashCode                         Method     int GetHashCode()
GetObject                           Method     T GetObject[T](System.Guid id, Microsoft.EnterpriseManagement.Common....
GetObjectHistoryTransactions        Method     System.Collections.Generic.IList[Microsoft.EnterpriseManagement.Commo...
GetObjectProjectionReader           Method     Microsoft.EnterpriseManagement.Common.IObjectProjectionReader[T] GetO...
GetObjectProjectionWithAccessRights Method     Microsoft.EnterpriseManagement.Common.EnterpriseManagementObjectProje...
GetObjectReader                     Method     Microsoft.EnterpriseManagement.Common.IObjectReader[T] GetObjectReade...
GetObjects                          Method     System.Collections.Generic.IList[T] GetObjects[T](Microsoft.Enterpris...
GetObjectWithAccessRights           Method     Microsoft.EnterpriseManagement.Common.EnterpriseManagementObject GetO...
GetParentObjects                    Method     System.Collections.Generic.IList[T] GetParentObjects[T](System.Guid i...
GetRelatedObjects                   Method     System.Collections.Generic.Dictionary[System.Guid,System.Collections....
GetRelationshipObject               Method     Microsoft.EnterpriseManagement.Common.EnterpriseManagementRelationshi...
GetRelationshipObjects              Method     System.Collections.Generic.IList[Microsoft.EnterpriseManagement.Commo...
GetRelationshipObjectsBySourceClass Method     System.Collections.Generic.IList[Microsoft.EnterpriseManagement.Commo...
GetRelationshipObjectsByTargetClass Method     System.Collections.Generic.IList[Microsoft.EnterpriseManagement.Commo...
GetRelationshipObjectsWhereSource   Method     System.Collections.Generic.IList[Microsoft.EnterpriseManagement.Commo...
GetRelationshipObjectsWhereTarget   Method     System.Collections.Generic.IList[Microsoft.EnterpriseManagement.Commo...
GetType                             Method     type GetType()
RefreshGroupMembers                 Method     System.Void RefreshGroupMembers(Microsoft.EnterpriseManagement.Config...
ToString                            Method     string ToString()

I have a little filter that I use when I want to see the a method, it’s a little different than the definition property of get-member output, and for me a little clearer.

filter Get-Definition
{
    param ( $MemberName )
    $_.GetType().GetMembers()|
    ?{ $_.name -eq $MemberName -and $_.membertype -eq "method"}|
    %{
    ($_.returntype.tostring() -replace "``1") + " " + $_.name + "[T] ("
    $_.GetParameters() | %{ 
            $s = @()
        }{ 
            $s += ,("   " + $_.parametertype + " " + $_.name) 
        }{
            $s -join ",`n"
        }
    "   )"
    }
}

Here’s what the output looks like for GetObjectReader.

PS> $emg.EntityObjects | get-definition GetObjectReader
Microsoft.EnterpriseManagement.Common.IObjectReader[T] GetObjectReader[T] (
   Microsoft.EnterpriseManagement.Common.EnterpriseManagementObjectGenericCriteria criteria,
   Microsoft.EnterpriseManagement.Common.ObjectQueryOptions queryOptions
   )
Microsoft.EnterpriseManagement.Common.IObjectReader[T] GetObjectReader[T] (
   Microsoft.EnterpriseManagement.Configuration.ManagementPackClass managementPackClass,
   Microsoft.EnterpriseManagement.Common.ObjectQueryOptions queryOptions
   )
Microsoft.EnterpriseManagement.Common.IObjectReader[T] GetObjectReader[T] (
   Microsoft.EnterpriseManagement.Common.EnterpriseManagementObjectCriteria criteria,
   Microsoft.EnterpriseManagement.Common.ObjectQueryOptions queryOptions
   )
Microsoft.EnterpriseManagement.Common.IObjectReader[T] GetObjectReader[T] (
   System.Collections.Generic.ICollection[Microsoft.EnterpriseManagement.Common.EnterpriseManagementObjectCri
teria] criteriaCollection,
   Microsoft.EnterpriseManagement.Common.ObjectQueryOptions queryOptions
   )
Microsoft.EnterpriseManagement.Common.IObjectReader[T] GetObjectReader[T] (
   Microsoft.EnterpriseManagement.Common.EnterpriseManagementObjectGenericCriteria criteria,
   Microsoft.EnterpriseManagement.Configuration.ManagementPackClass mpClass,
   Microsoft.EnterpriseManagement.Common.ObjectQueryOptions queryOptions
   )
Microsoft.EnterpriseManagement.Common.IObjectReader[T] GetObjectReader[T] (
   System.Collections.Generic.ICollection[System.Guid] ids,
   Microsoft.EnterpriseManagement.Common.ObjectQueryOptions queryOptions
   )

This is the method that will do what I need, it will return a reader (which I can use to turn the results into a collection) and there is one overload that takes a management pack class and some options.  This will return all the instances of a particular class, based on a set of passed options, which in my case will be our default options (return everything).

In my case, all I need now is a Management Pack Class (which means I can use my earlier Get-SMClass script).  In my case I just need to wrap the call to GetObjectReader in a fairly simple cmdlet and viola!

01 using System;
02 using System.Management.Automation;
03 using Microsoft.EnterpriseManagement;
04 using Microsoft.EnterpriseManagement.Common;
05 using Microsoft.EnterpriseManagement.Configuration;
06 namespace ServiceManager.Powershell.Demo
07 {
08     [Cmdlet("Get","SCSMObject")]
09     public class GetSMObjectCommand : PSCmdlet
10     {
11         private ManagementPackClass _class = null;
12         [Parameter(ParameterSetName="Class",Position=0,Mandatory=true,ValueFromPipeline=true)]
13         public ManagementPackClass Class
14         {
15             get { return _class; }
16             set { _class = value; }
17         }
18         private EnterpriseManagementGroup _mg;
19         protected override void BeginProcessing()
20         {
21             _mg = new EnterpriseManagementGroup("localhost");
22         }
23         protected override void ProcessRecord()
24         {
25             foreach(EnterpriseManagementObject o in 
26                     _mg.EntityObjects.GetObjectReader<EnterpriseManagementObject>(Class,ObjectQueryOptions.Default) 
27                 ) 
28             {
29                 WriteObject(o);
30             }
31         }
32     }
33 }

A couple of things to note here:

  1. I’m assuming that I’m running on the server, in line 21 where I create the EnterpriseManagementGroup object, I use “localhost” as the hostname.
  2. I set the ValueFromPipeline attributed on the ManagementPackClass parameter to true, which will allow me to pipe the result of Get-SMClass to Get-SCSMObject (line 12).
  3. I use GetObjectReader and emit each of the objects explicitly (line 29)

Woo!  Let’s give it a go!

PS> get-smclass windows.computer$|get-scsmobject


PropertyAccessRights : Unknown
Parent               : {WIN-752HJBSX24M.woodgrove.com, WIN-752HJBSX24M.woodgrove.com, WIN-752HJBSX24M, WOODGROVE...}
Type                 : PrincipalName
Value                : WIN-752HJBSX24M.woodgrove.com
Id                   : 00000000-0000-0000-0000-000000000000
ManagementGroup      : ServiceManagerMgmtGroup1
ManagementGroupId    : 8884fff9-00d4-abcc-6620-e6d25f39e0d6

...

PropertyAccessRights : Unknown
Parent               : {WIN-752HJBSX24M.woodgrove.com, WIN-752HJBSX24M.woodgrove.com, WIN-752HJBSX24M, WOODGROVE...}
Type                 : DisplayName
Value                : WIN-752HJBSX24M.woodgrove.com
Id                   : 00000000-0000-0000-0000-000000000000
ManagementGroup      : ServiceManagerMgmtGroup1
ManagementGroupId    : 8884fff9-00d4-abcc-6620-e6d25f39e0d6

This doesn’t look quite like what I wanted – what I want is a table of the properties of the windows.computer class. Let’s investigate:

PS> $o = get-smclass windows.computer$|get-scsmobject
PS> $o|gm


   TypeName: Microsoft.EnterpriseManagement.Common.EnterpriseManagementSimpleObject

Name                 MemberType Definition
----                 ---------- ----------
Equals               Method     bool Equals(System.Object obj)
GetHashCode          Method     int GetHashCode()
...
ManagementGroup      Property   Microsoft.EnterpriseManagement.EnterpriseManagementGroup ManagementGroup {get;}
ManagementGroupId    Property   System.Guid ManagementGroupId {get;}
Parent               Property   Microsoft.EnterpriseManagement.Common.EnterpriseManagementObjectBaseWithProperties P...
PropertyAccessRights Property   Microsoft.EnterpriseManagement.Common.EnterpriseManagementObjectAccessRights Propert...
Type                 Property   Microsoft.EnterpriseManagement.Configuration.ManagementPackProperty Type {get;}
Value                Property   System.Object Value {get;set;}

This isn’t certainly isn’t what I wanted, I should be getting an EnterpriseManagementObject not an EnterpriseManagementSimpleObject, perhaps piping the input is a problem.

PS> gm -input $o


   TypeName: Microsoft.EnterpriseManagement.Common.EnterpriseManagementObject

Name                                         MemberType            Definition
----                                         ----------            ----------
ApplyTemplate                                Method                System.Void ApplyTemplate(Microsoft.EnterpriseMan...
Commit                                       Method                System.Void Commit(Microsoft.EnterpriseManagement...
ContainsProperty                             Method                bool ContainsProperty(Microsoft.EnterpriseManagem...
CreateNavigator                              Method                System.Xml.XPath.XPathNavigator CreateNavigator()
Equals                                       Method                bool Equals(System.Object obj)
GetClasses                                   Method                System.Collections.Generic.IList[Microsoft.Enterp...
GetEnumerator                                Method                System.Collections.Generic.IEnumerator[Microsoft....
...
FullName                                     Property              System.String FullName {get;}
Id                                           Property              System.Guid Id {get;}
LastModified                                 Property              System.DateTime LastModified {get;}
LastModifiedBy                               Property              System.Nullable`1[[System.Guid, mscorlib, Version...
LeastDerivedNonAbstractManagementPackClassId Property              System.Guid LeastDerivedNonAbstractManagementPack...
ManagementGroup                              Property              Microsoft.EnterpriseManagement.EnterpriseManageme...
ManagementGroupId                            Property              System.Guid ManagementGroupId {get;}
...

ah – there’s the culprit – EnterpriseManagementObject has a GetEnumerator method, which will get called when the formatter gets invoked (or when it hits the pipeline).  We can keep change our formatting a bit to see the information differently:

PS> fl -input $o -expand coreonly


Name                                         : WIN-752HJBSX24M.woodgrove.com
Path                                         :
DisplayName                                  : WIN-752HJBSX24M.woodgrove.com
FullName                                     : Microsoft.Windows.Computer:WIN-752HJBSX24M.woodgrove.com
ManagementPackClassIds                       : {7b070bc5-0f54-6663-f840-17affa1d6304, a754daf6-0e0e-9fd3-beef-9da66b39e
                                               41f, 139a4e2d-47fe-ac19-02c4-a31210c457cc, 9f178faf-54f4-b13b-6b32-a684d
                                               c6d3ec1...}
LeastDerivedNonAbstractManagementPackClassId : 7b070bc5-0f54-6663-f840-17affa1d6304
TimeAdded                                    : 7/1/2009 11:23:41 PM
LastModifiedBy                               : 7431e155-3d9e-4724-895e-c03ba951a352
LastModified                                 : 7/2/2009 9:50:34 PM
Id                                           : e4011629-ae31-a8a6-9bee-99980c68ba69
ManagementGroup                              : ServiceManagerMgmtGroup1
ManagementGroupId                            : 8884fff9-00d4-abcc-6620-e6d25f39e0d6

Well, this is better (at least I’m getting a single list rather than multiple objects), but it’s not still not quite right.  What’s I really want is the values of the various properties of  object from the Service Manager perspective.  This information is kept in the collection of properties (available via the GetProperties method).  This is what I was seeing at first, just in a format I wasn’t expecting.  Let’s try a different approach, I noticed that when I saw the data flashing by the first time, it looked like a couple of properties were interesting.   If I select the Type and Value properties of the EnterpriseSimpleObject, that may be what I want.

PS> get-smclass windows.computer$|get-scsmobject|format-table Type,Value -au

Type                            Value
----                            -----
PrincipalName                   WIN-752HJBSX24M.woodgrove.com
DNSName                         WIN-752HJBSX24M.woodgrove.com
NetbiosComputerName             WIN-752HJBSX24M
NetbiosDomainName               WOODGROVE
IPAddress
NetworkName                     WIN-752HJBSX24M.woodgrove.com
ActiveDirectoryObjectSid
IsVirtualMachine                True
DomainDnsName
OrganizationalUnit
ForestDnsName
ActiveDirectorySite
LogicalProcessors
OffsetInMinuteFromGreenwichTime
LastInventoryDate
InstallDirectory
IsVirtualNode
ObjectStatus
AssetStatus
Notes
DisplayName                     WIN-752HJBSX24M.woodgrove.com

OK!  this is more along the lines that I was thinking.  It’s the representation of the information about the computer that I was expecting.  However, having to do this formatting is a pain, and it’s not possible for me to refer to a windows.computer objects properties the way I want, so what I will do is build a simple-minded data adapter, using PowerShell’s capability for building objects on the fly.

  1 using System;
  2 using System.Management.Automation;
  3 using Microsoft.EnterpriseManagement;
  4 using Microsoft.EnterpriseManagement.Common;
  5 using Microsoft.EnterpriseManagement.Configuration;
  6 namespace ServiceManager.Powershell.Demo
  7 {
  8     [Cmdlet("Get","SCSMObject")]
  9     public class GetSMObjectCommand : PSCmdlet
 10     {
 11         private ManagementPackClass _class = null;
 12         [Parameter(ParameterSetName="Class", Position=0,Mandatory=true,ValueFromPipeline=true)]
 13         public ManagementPackClass Class
 14         {
 15             get { return _class; }
 16             set { _class = value; }
 17         }
 18         private EnterpriseManagementGroup _mg;
 19         protected override void BeginProcessing()
 20         {
 21             _mg = new EnterpriseManagementGroup("localhost");
 22         }
 23         protected override void ProcessRecord()
 24         {
 25             foreach(EnterpriseManagementObject o in
 26                     _mg.EntityObjects.GetObjectReader(Class,ObjectQueryOptions.Default)
 27                 )
 28             {
 29                 PSObject pso = new PSObject();
 30                 pso.Properties.Add(new PSNoteProperty("__base",o));
 31                 pso.TypeNames.Insert(0, "EnterpriseManagementObject#"+Class.Name);
 32                 foreach ( ManagementPackProperty p in o.GetProperties())
 33                 {
 34                     pso.Properties.Add(new PSNoteProperty(p.Name, o[p].Value));
 35                 }
 36                 WriteObject(pso);
 37             }
 38         }
 39     }
 40 }

So, it’s a fairly simple change, an additional few lines of code (lines 29-36) and it should be what we want.  Just a couple of things to note:

  1. I save the entire original object in a NoteProperty called “__base”
  2. I add a new string to the TypeNames collection, this way I can create formatting based on the Service Manager class.  This is similar to what the PowerShell team did for WMI.
  3. For each one of the Service Manager properties in the class, I created a NoteProperty (lines 32-35)

Let’s see what we have now!

PS> get-smclass windows.computer$|get-scsmobject


__base                          : {WIN-752HJBSX24M.woodgrove.com, WIN-752HJBSX24M.woodgrove.com, WIN-752HJBSX24M, WOODG
                                  ROVE...}
PrincipalName                   : WIN-752HJBSX24M.woodgrove.com
DNSName                         : WIN-752HJBSX24M.woodgrove.com
NetbiosComputerName             : WIN-752HJBSX24M
NetbiosDomainName               : WOODGROVE
IPAddress                       :
NetworkName                     : WIN-752HJBSX24M.woodgrove.com
ActiveDirectoryObjectSid        :
IsVirtualMachine                : True
DomainDnsName                   :
OrganizationalUnit              :
ForestDnsName                   :
ActiveDirectorySite             :
LogicalProcessors               :
OffsetInMinuteFromGreenwichTime :
LastInventoryDate               :
InstallDirectory                :
IsVirtualNode                   :
ObjectStatus                    :
AssetStatus                     :
Notes                           :
DisplayName                     : WIN-752HJBSX24M.woodgrove.com

Ah!  This is much better!  Now I’m getting the interesting properties of the instance.  Let’s try get-member

PS> get-smclass windows.computer$|get-scsmobject|gm


   TypeName: EnterpriseManagementObject#Microsoft.Windows.Computer

Name                            MemberType   Definition
----                            ----------   ----------
Equals                          Method       bool Equals(System.Object obj)
GetHashCode                     Method       int GetHashCode()
GetType                         Method       type GetType()
ToString                        Method       string ToString()
ActiveDirectoryObjectSid        NoteProperty  ActiveDirectoryObjectSid=null
ActiveDirectorySite             NoteProperty  ActiveDirectorySite=null
AssetStatus                     NoteProperty  AssetStatus=null
DisplayName                     NoteProperty System.String DisplayName=WIN-752HJBSX24M.woodgrove.com
DNSName                         NoteProperty System.String DNSName=WIN-752HJBSX24M.woodgrove.com
DomainDnsName                   NoteProperty  DomainDnsName=null
ForestDnsName                   NoteProperty  ForestDnsName=null
InstallDirectory                NoteProperty  InstallDirectory=null
IPAddress                       NoteProperty  IPAddress=null
IsVirtualMachine                NoteProperty System.Boolean IsVirtualMachine=True
IsVirtualNode                   NoteProperty  IsVirtualNode=null
LastInventoryDate               NoteProperty  LastInventoryDate=null
LogicalProcessors               NoteProperty  LogicalProcessors=null
NetbiosComputerName             NoteProperty System.String NetbiosComputerName=WIN-752HJBSX24M
NetbiosDomainName               NoteProperty System.String NetbiosDomainName=WOODGROVE
NetworkName                     NoteProperty System.String NetworkName=WIN-752HJBSX24M.woodgrove.com
Notes                           NoteProperty  Notes=null
ObjectStatus                    NoteProperty  ObjectStatus=null
OffsetInMinuteFromGreenwichTime NoteProperty  OffsetInMinuteFromGreenwichTime=null
OrganizationalUnit              NoteProperty  OrganizationalUnit=null
PrincipalName                   NoteProperty System.String PrincipalName=WIN-752HJBSX24M.woodgrove.com
__base                          NoteProperty Microsoft.EnterpriseManagement.Common.EnterpriseManagementObject __base...

And finally, let’s take a look at this as a table.

PS> get-smclass windows.computer$|get-smobject|ft Type,DisplayName
       
Type                               DisplayName                                      
----                               -----------                                      
Microsoft.Windows.Computer         Client4.woodgrove.com                            
Microsoft.Windows.Computer         Client5.woodgrove.com                            
Microsoft.Windows.Computer         Client3.woodgrove.com                            
Microsoft.Windows.Computer         Server1.woodgrove.com                            
Microsoft.Windows.Computer         Server3.woodgrove.com                            
Microsoft.Windows.Computer         Server2.woodgrove.com                            
Microsoft.Windows.Computer         WIN-752HJBSX24M.woodgrove.com                    
Microsoft.Windows.Computer         Client1.woodgrove.com                            
Microsoft.Windows.Computer         Client2.woodgrove.com 

This is a great start, and we’ll use this as a starting point for the future!  I’ve put all the module files in my Sky Drive, from here on I’ll be adding features to the module.

Posted July 27, 2009 by jtruher3 in ServiceManager