Archive for October 2009

Batch Operations in Service Manager 2010 with PowerShell – Removing Instances   2 comments

Sometimes, when I am developing a demo for Service Manager, I wind up creating a lot of Service Requests or Incidents when I’m trying to get the demo just right. However, after I’ve gotten everything working just like I want and then I give the demo, I don’t really want to have all those earlier things visible because they get in the way of the what I’m trying to show. The Service Manager 2010 provides a way to removing instances from the console, and I could use that, but I like to script everything so I want to create a script instead of using the UI. With this script, I can use this to remove any instance in the CMDB, including Incidents and Service Requests. 

Our programming interfaces provide a way to remove instances and I’ve written my script to work a couple of ways:

  • If you provide the script with  ClassName parameter, the script will remove every instance of that class!
  • If you pipe an EnterpriseManagementObject at the script, the script will remove that instance

These are pretty big hammers, so I’ve made sure that you can use –WhatIf and I’ve also set ConfirmImpact as High which will ask for confirmation even if you don’t specify –confirm. My last warning is that you should not put this script anywhere near your production machines. It will remove the data forever, so be sure you are careful!!

I think the most interesting bit of the script is on line 21. This is where an IncrementalDiscoveryData object is created. The IncrementalDiscoveryData object allows you to deal with instances in bulk.  I can use this object to remove instances then remove them all by the single call to Commit in line 73.  The code between lines 29 and 35 represent the code that’s needed to call our generic methods, the script uses reflection to build the generic method and then call it.

The PROCESS block starting on line 50 handles the case when you pipe objects to the script. It first checks to be sure that it’s an EnterpriseManagementObject, and if so, adds the object to the IncrementalDiscoveryData collection which will be used in the END block. Rather than wrapping the call to Commit in another ShouldProcess block, I just check to be sure I have objects to remove. If there are, I make the Commit call. I don’t like it when my scripts ask me “Do you really want to do this” after I’ve already answered it once.

This script is a PowerShell version 2.0 script (as seen in line 1). This way I can take advantage of the ConfirmImpact and the other PowerShell 2.0 goodies.

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
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
105
106
#requires -version 2.0
[CmdletBinding(SupportsShouldProcess=$true, ConfirmImpact="High")]
param ( 
    [Parameter(Position=0)]
    $classname,
    [Parameter(ValueFromPipeline=$true)]$EMO
    )
BEGIN
{
    # oh for a way to specify namespaces
    $NS = "Microsoft.EnterpriseManagement"
    if ( ! ("${NS}.Common.EnterpriseManagementObject" -as "type"))
    {
        [reflection.assembly]::LoadWithPartialName("${NS}.Core")
    }
    
    $LFX          = "ConnectorFramework"
    $DEFAULT      = ("${NS}.Common.ObjectQueryOptions" -as "type")::Default
    $EMOT         = "${NS}.Common.EnterpriseManagementObject" -as "type"
    $EMG          = new-object "${NS}.EnterpriseManagementGroup" localhost
    $IDD          = new-object "${NS}.${LFX}.incrementaldiscoverydata"
    $guid         = $EMG.ConnectorFramework.GetDefaultConnectorId().guid
    $SDKConnector = $EMG.ConnectorFramework.GetConnector($guid)
    $REMOVECOUNT  = 0
    # only go through this process if you got a classname and are
    # going to remove all instances of that class
    if ( $classname )
    {
        $IMgmt    = $EMG.EntityObjects.GetType()
        $class = $EMG.EntityTypes.GetClasses()|?{$_.name -eq $classname}
        [array]$arguments = ($class -as "${NS}.Configuration.ManagementPackClass"),$DEFAULT
        [type[]]$TYPES = ("${NS}.Configuration.ManagementPackClass" -as "type"),
                     ("${NS}.Common.ObjectQueryOptions" -as "type")
        $ObjectReader = $IMgmt.getmethod("GetObjectReader",$TYPES)
        $GenericMethod = $ObjectReader.MakeGenericMethod($EMOT)
        if ( ! $class ) { throw "no class $classname" }
        # GET THE OBJECTS
        $SMObjects = $GenericMethod.invoke($EMG.EntityObjects,$arguments) 
        if ( ! $SMObjects ) { "No objects to remove"; exit }
        $SMObjects|%{ 
            if ( $PSCmdlet.ShouldProcess( $_.displayname ) )
            {
                $REMOVECOUNT++
                $IDD.Remove($_)
            }
        } 
    }
}

PROCESS
{
    if ( $EMO -is "${NS}.Common.EnterpriseManagementObject")
    {
        if ( $PSCmdlet.ShouldProcess( $EMO.displayname ) )
        {
            $REMOVECOUNT++
            $IDD.Remove($EMO)
        }
    }
    elseif ( ! $EMO ) { ; }
    else
    {
        Write-Error "$_ is not an EnterpriseManagementObject, skipping"
    }
}

END
{
    # only actually call this if there are any to delete
    if ( $REMOVECOUNT )
    {
        Write-Verbose "Committing Changes"
        $IDD.Commit(${SDKConnector})
    }
}

<#
.SYNOPSIS
    Remove an instance from the Service Manager 2010 CMDB
.DESCRIPTION
    The cmdlet removes instances from the Service Manager CMDB.
    If the classname parameter is provided, every instance will
    be removed from the CMDB.
    Optionally, instances may be piped to this cmdlet in which case
    only those instances will be removed.
.PARAMETER ClassName
    A Service Manager 2010 class name
.PARAMETER EMO
    An instance to be removed from the Service Mangaer 2010 CMDB
.EXAMPLE
remove-smobject -classname Microsoft.Windows.Computer
Removes all instances of Microsoft.Windows.Computer from the
Service Manager 2010 CMDB
.EXAMPLE
get-smobject Microsoft.Windows.Computer | ?{$_.displayname -match "Computer00"}|remove-smobject
Removes all instances of Microsoft.Windows.Computer from the
Service Manager 2010 CMDB where the displayname matches "Computer00"
.INPUTS
    Output from get-smobject
    Any EnterpriseManagementObject
.OUTPUTS
    None
.LINK
    get-smobject-ManagementPack
    get-smclass
#>

Here’s an example of removing every Microsoft.Windows.Computer from Service Manager (I’m not actually going to do this, so I’m using –Whatif). If you need a reminder, Get-SmObject.ps1 was a blog posting here.

PS> ./get-smobject microsoft.windows.computer|./remove-smobject -whatif
What if: Performing operation "remove-smobject.ps1" on Target "Computer028".
What if: Performing operation "remove-smobject.ps1" on Target "Computer008".
What if: Performing operation "remove-smobject.ps1" on Target "computer1.contoso.com".
What if: Performing operation "remove-smobject.ps1" on Target "Computer027".
What if: Performing operation "remove-smobject.ps1" on Target "Computer001".
What if: Performing operation "remove-smobject.ps1" on Target "WIN-752HJBSX24M.woodgrove.com".
What if: Performing operation "remove-smobject.ps1" on Target "Computer002".
What if: Performing operation "remove-smobject.ps1" on Target "Computer024".
What if: Performing operation "remove-smobject.ps1" on Target "Computer030".
What if: Performing operation "remove-smobject.ps1" on Target "Computer007".
What if: Performing operation "remove-smobject.ps1" on Target "Computer023".
What if: Performing operation "remove-smobject.ps1" on Target "Computer025".
What if: Performing operation "remove-smobject.ps1" on Target "Computer026".
What if: Performing operation "remove-smobject.ps1" on Target "Computer003".
What if: Performing operation "remove-smobject.ps1" on Target "Computer029".

If I want to remove one computer, I can just filter for what I want.

PS> ./get-smobject microsoft.windows.computer|?{$_.displayname -match "Computer028"}|
>> ./remove-smobject -whatif
What if: Performing operation "remove-smobject.ps1" on Target "Computer028".

This is what it will look like when you really remove it!

Computer028 is gone! Notice that this is really where PowerShell provides lots of value, the interaction to confirm the removal is done with the Cmdlet attribute in line 2 – ConfirmImpact=”High”, that plus the $PSCmdlet.ShouldProcess in lines 41 and 54 make it really easy to write scripts that won’t shoot me in the foot!

Advertisements

Posted October 22, 2009 by jtruher3 in ServiceManager

Service Manager and the PowerShell 1 liner   Leave a comment

I’ve written a number of fairly complicated scripts for Service Manager over the last few months, and while talking to a team-mate about something he wanted to do, it looked like it was just 1-line PowerShell script. That got me thinking about what other things in Service Manager could be handled by a really simple (say less than 5 lines) of PowerShell. The list is good sized, so I thought it would be good if I shared them.

The problem we had at hand was how I could help one our development partners figure out in which management pack a particular class resides. It turns out it was 3 lines of script to find out.

PS> [reflection.assembly]::LoadWithPartialName("Microsoft.EnterpriseManagement.Core")

GAC    Version        Location
---    -------        --------
True   v2.0.50727     C:\Windows\assembly\GAC_MSIL\Microsoft.EnterpriseManagement.Core\7.0.5000.0__31bf3856ad364e35\...

PS> $EMG = new-object Microsoft.EnterpriseManagement.EnterpriseManagementGroup localhost
PS> $EMG.EntityTypes.GetClasses()|ft name,{$_.GetManagementPack().Name}

Name                                                        $_.GetManagementPack().Name
----                                                        ---------------------------
System.Entity                                               System.Library
System.Collections                                          System.Library
System.ConfigItem                                           System.Library
System.LogicalEntity                                        System.Library
. . .

the business end of the script is just the last line. The first two lines are just what I need to get access to the Service Manager Data Access Service. I do this so much that put those two lines in my $profile.

After that, it’s just a matter of adding a filter to find out the actual class of interest.

PS> $EMG.EntityTypes.GetClasses()|?{$_.name -match "System.Knowledge.Article"}|ft name,{$_.GetManagementPack().Name}

Name                                                        $_.GetManagementPack().Name
----                                                        ---------------------------
System.Knowledge.Article                                    System.Knowledge.Library

Now, the label for the second column may not pretty, but I’m not fussed about that, I’ve got the data that I need. The next thing I needed to figure out was the properties of this class. That’s another one liner:

PS> $emg.entitytypes.GetClasses()|?{$_.name -match "System.Knowledge.Article"}|
>> select -expand propertycollection|ft name,key,type -au
>>

Name                Key     Type
----                ---     ----
ArticleType       False     enum
ArticleTemplate   False   string
ArticleOwner      False   string
Category          False     enum
Comments          False   string
CreatedDate       False datetime
CreatedBy         False   string
PrimaryLocaleID   False      int
Status            False     enum
Tag               False     enum
VendorArticleID   False   string
Title             False   string
Abstract          False   string
Keywords          False   string
ArticleId          True   string
EndUserContent    False   binary
AnalystContent    False   binary
ExternalURLSource False   string
ExternalURL       False   string

(ok, so it’s a long line, but it’s still a single pipeline)

This doesn’t quite tell the whole story, because if I wanted to create one of these classes, I may have more properties available to me (based on the base classes for the class I want). That’s just *2* lines:

PS> $class = $emg.entitytypes.GetClasses()|?{$_.name -match "System.Knowledge.Article"}
PS> (new-object microsoft.enterprisemanagement.common.CreatableEnterpriseManagementObject $emg,$class).GetProperties()|
>> ft name,key,type -au
>>

Name                Key     Type
----                ---     ----
ArticleType       False     enum
ArticleTemplate   False   string
ArticleOwner      False   string
Category          False     enum
Comments          False   string
CreatedDate       False datetime
CreatedBy         False   string
PrimaryLocaleID   False      int
Status            False     enum
Tag               False     enum
VendorArticleID   False   string
Title             False   string
Abstract          False   string
Keywords          False   string
ArticleId          True   string
EndUserContent    False   binary
AnalystContent    False   binary
ExternalURLSource False   string
ExternalURL       False   string
ObjectStatus      False     enum
AssetStatus       False     enum
Notes             False richtext
DisplayName       False   string

I save the class and then use it to create the object I want with new-object.

Sometimes, I need to know which management pack an enumeration is in. Another 1 liner:

PS> $emg.EntityTypes.GetEnumerations()|?{$_.name -match "high"}|ft name,{$_.getmanagementpack().name} -au

Name                                                    $_.getmanagementpack().name
----                                                    ---------------------------
System.WorkItem.TroubleTicket.ImpactEnum.High           System.WorkItem.Library
System.WorkItem.TroubleTicket.UrgencyEnum.High          System.WorkItem.Library
System.ServiceManagement.ServicePriority.High           ServiceManager.ServiceMaps.Configuration
IncidentResolutionCategoryEnum.FixedByHigherTierSupport ServiceManager.IncidentManagement.Configuration
ChangePriorityEnum.High                                 ServiceManager.ChangeManagement.Configuration
ChangeRiskEnum.High                                     ServiceManager.ChangeManagement.Configuration
ActivityPriorityEnum.High                               ServiceManager.ActivityManagement.Configuration

One of my early examples for retrieving management packs. That’s a 1 liner:

PS> $emg.ManagementPacks.GetManagementPacks()|ft Sealed,Version,Name
Sealed Version    Name
------ -------    ----
 False 7.0.5228.0 Microsoft.SystemCenter.ServiceManager.Connector.Configuration
  True 7.0.5228.0 Microsoft.SystemCenter.Internal
  True 7.0.5228.0 ServiceManager.Reporting.Help
  True 7.0.5228.0 Microsoft.SystemCenter.Report.Library
  True 7.0.5228.0 ServiceManager.LinkingFramework.Library
  True 7.0.5228.0 System.ApplicationLog.Library
  True 7.0.5228.0 ServiceManager.IncidentManagement.Library.Datawarehouse
  True 7.0.5228.0 Microsoft.EnterpriseManagement.ServiceManager.UI.Console
  True 7.0.5228.0 ServiceManager.ActivityManagement.Library.Datawarehouse
  True 7.0.5228.0 ServiceManager.ChangeManagement.Library
  True 7.0.5228.0 ServiceManager.IncidentManagement.Report.Library
  True 7.0.5228.0 ServiceManager.ChangeManagement.Report.Library
. . .

What if I wanted to remove a management pack? Before I do, I had better find out whether it’s possible, as if other Management Packs depend on the one I want to remove. So I need to find the dependent management packs – 2 lines!

PS> $crLib = $emg.ManagementPacks.GetManagementPacks()|?{$_.name -eq "System.WorkItem.ChangeRequest.Library"}
PS> $emg.ManagementPacks.GetDependentManagementPacks($crLib)|ft sealed,version,name -au

Sealed Version    Name
------ -------    ----
  True 7.0.5228.0 ServiceManager.ActivityManagement.Library.Datawarehouse
  True 7.0.5228.0 ServiceManager.ChangeManagement.Library
  True 7.0.5228.0 ServiceManager.ChangeManagement.Report.Library
  True 7.0.5228.0 ServiceManager.ServiceMaps.Library
  True 7.0.5228.0 ServiceManager.ChangeManagement.Library.Datawarehouse
 False 7.0.5228.0 ServiceManager.ConfigurationManagement.Configuration
  True 7.0.5228.0 ServiceManager.ChangeManagement.Help
  True 7.0.5228.0 Microsoft.SystemCenter.ServiceManager.Portal
 False 7.0.5228.0 ServiceManager.ChangeManagement.Configuration

In this case, I won’t be able to remove the ChangeRequest Library, because of all the dependencies, but if I have a management pack that is not needed by other management packs, removing the management pack is another one-liner:

PS> $emg.ManagementPacks.GetManagementPacks()|?{$_.name -eq "MPToRemove"}|
>> %{ $emg.ManagementPacks.UninstallManagementPack($_) }
>>

Perhaps I want to find out how much localization I need to do. To understand how much work I will need to do, I should find out how many lines of text I need to localize.  How do I find out how many different English display strings are stored in my management packs? 1 line!

PS> ($emg.LanguagePacks.getlanguagepacks()|?{$_.name -eq "ENU"}|select-object -Expand DisplayStringCollection).count
6456

and if I wanted to know the count of my display strings for each language? 1 line!

PS> $emg.LanguagePacks.getlanguagepacks()|select-object -ExpandProperty DisplayStringCollection |
>> Group-Object LanguageCode|format-table Count,Name -au
>>

Count Name
----- ----
 6456 ENU
 6455 DEU
 6282 JPN

and what if I wanted to see the actual English display strings from the System.Library management pack? Just another line!

PS> $emg.LanguagePacks.GetLanguagePacks()|?{$_.Name -eq "ENU" -and $_.GetManagementPack().Name -eq "System.Library"}|
>> Select-Object -Expand DisplayStringCollection|ft name,description
>>

Name                                                        Description
----                                                        -----------
Display Name                                                Display name
Timeout Seconds
Database                                                    Defines the basic properties of databases
Local Application                                           Defines the basic properties of applications that are di...
Reference                                                   Defines the basic properties of directed relationships
. . .

yow!

Posted October 2, 2009 by jtruher3 in ServiceManager