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 END <# |
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!

Hi Jerry,
Great script here. What is the script for deleting in ranges? Is it possible to do that?
Thanks,
Kay
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.