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!

About these ads

Posted October 22, 2009 by jtruher3 in ServiceManager

2 responses to “Batch Operations in Service Manager 2010 with PowerShell – Removing Instances

Subscribe to comments with RSS.

  1. Hi Jerry,

    Great script here. What is the script for deleting in ranges? Is it possible to do that?

    Thanks,
    Kay

  2. This is great! It deletes all of the CIs that I tell it to just fine, however, how do you remove the History entries for those items? Whenever I readd those items to the CMDB, since they have the same primary key, all of the old history entries reattach to the CIs I import.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: