Menu Close

Demystifying Remove-SCOMDisabledClassInstance

image

I have seen many discussions over the years about how to use this command, and I’d like to clarify some things.

What does this command do?

The best description is probably right here in our product documentation:  “The Remove-SCOMDisabledClassInstance cmdlet deletes class instances for which you previously disabled discovery.

Ok, what exactly does that mean?

It means, that this command ONLY deletes discovered objects from SCOM, if the discovery that discovered them is DISABLED with an Override.  That’s it.  That’s all it does.

 

Lets say you disabled a discovery for a specific SQL server, or a Group of Windows Computers using an override.  This override will simply tell the agent to “stop running that discovery on that target”.  It will never clean up/delete the previously discovered objects.  That’s where Remove-SCOMDisabledClassInstance works.  When this command is called, it first evaluates all Discoveries in the management group that have Enabled = False overrides.  Once that is collected, it determines if any discovered objects exist that are disabled via these overrides, then deletes those objects, and any relationships that involve those objects.

There is not really a need to run this command on a schedule, but it doesn’t hurt anything either.  It wont make things run any cleaner, unless you regularly apply new overrides to disable discoveries, or you have discovery disabling overrides using groups, and that group membership changes.

I have seen people think after they delete an MP they should run this, or when something isn’t working right with SCOM they should run this…. and that just isn’t correct.

 

One of the BIG negatives on this command, is the fact it prompts you for Yes or No when you call it.

image

 

If you need to schedule it, or put it in a script without the prompt – you can accomplish the same thing by calling this:

# Begin [Reflection.Assembly]::Load("Microsoft.EnterpriseManagement.Core, Version=7.0.5000.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35") | Out-Null [Reflection.Assembly]::Load("Microsoft.EnterpriseManagement.OperationsManager, Version=7.0.5000.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35") | Out-Null $mg = [Microsoft.EnterpriseManagement.ManagementGroup]::Connect("localhost") $mg.EntityObjects.DeleteDisabledObjects() # End

 

Another challenge, is when you have a LOT of items to delete.  Sometimes a management pack discovers LOTS of instances, and when you disable the discovery, this will just time out, or throw an error while running about receiving discovery data that interrupts this process.  If that happens, it would be nice to run this in a loop so we don’t have to babysit the cleanup process.  Here is an example of exactly that:

# Begin $TotalLoopsToRun = 100 [Reflection.Assembly]::Load("Microsoft.EnterpriseManagement.Core, Version=7.0.5000.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35") | Out-Null [Reflection.Assembly]::Load("Microsoft.EnterpriseManagement.OperationsManager, Version=7.0.5000.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35") | Out-Null $mg = [Microsoft.EnterpriseManagement.ManagementGroup]::Connect("localhost") $Error.Clear() $LoopCounter = 1 $RunAgain = $true WHILE ($LoopCounter -le $TotalLoopsToRun -and $RunAgain -eq $true) { Write-Host "Loop number: $LoopCounter" IF ($LoopCounter -eq 1) { $LoopCounter++ #First run always calls the delete command $mg.EntityObjects.DeleteDisabledObjects() IF ($Error) {$RunAgain = $true} ELSE {$RunAgain = $false} } ELSE { $Error.Clear() $mg.EntityObjects.DeleteDisabledObjects() IF ($Error) {$RunAgain = $true} ELSE {$RunAgain = $false} } $LoopCounter++ } # End

Simply edit TotalLoopsToRun to the maximum number of loops you want to attempt.  This script will run the delete command, then if there is an error, it will keep running it in a loop.  The loop will continue until the command finishes without errors, or until the number of loops is exhausted.

 

To understand how many discoveries you have with overrides for Enabled = false, you can run this SQL query:

--Begin Query --Gather all overrides specific to disabled discoveries Select WorkflowType, WorkflowName, Overview.OverrideName, OverrideableParameterName, OverrideValue, OverrideDescription, OverrideEnforced, OverrideScope, TargetedInstanceName, TargetedInstancePath, ORMPName, ORMPDescription, ORMPSealed, MPTargetClass, TargetManagementPack, ModuleOverrideId, ORMPLanguage, OverrideLastModified, OverrideCreatedOn from ( SELECT 'Discovery' AS 'WorkflowType', dv.displayname AS WorkflowName, OverrideName, op.OverrideableParameterName, mo.value AS OverrideValue, lt.ltvalue AS OverrideDescription, mo.enforced AS OverrideEnforced, mt.typename AS OverrideScope, bme.displayname AS TargetedInstanceName, bme.path AS TargetedInstancePath, mpv.displayname AS ORMPName, mpv.description AS ORMPDescription, mpv.sealed AS ORMPSealed, mpv.LanguageCode AS ORMPLanguage, mo.lastmodified AS OverrideLastModified, mo.timeadded AS OverrideCreatedOn --mpv.TimeCreated AS MPTimeCreated FROM moduleoverride mo INNER JOIN managementpackview mpv ON mpv.id = mo.managementpackid INNER JOIN discoveryview dv ON dv.id = mo.parentid INNER JOIN managedtype mt ON mt.managedtypeid = mo.typecontext LEFT JOIN localizedtext lt ON lt.ltstringid = mo.moduleoverrideid LEFT JOIN basemanagedentity bme ON bme.basemanagedentityid = mo.instancecontext LEFT JOIN overrideableparameter op ON mo.overrideableparameterid = op.overrideableparameterid --Where (lt.LTStringType = 2 and mpv.LanguageCode = 'ENU') --Where (mpv.Sealed = 0 and mpv.LanguageCode = 'ENU') )Overview left JOIN ( SELECT mo.ModuleOverrideId, mo.OverrideName, mpv.DisplayName as 'MPTargetClass', mpv.FriendlyName as [TargetManagementPack] FROM ModuleOverride mo INNER JOIN Managedtype mt on mt.ManagedTypeId = mo.TypeContext INNER JOIN ManagementPackView mpv on mpv.ID = mt.ManagementPackId Where mpv.LanguageCode = 'ENU' ) OverridesOverview ON OverridesOverview.OverrideName = Overview.OverrideName WHERE OverrideableParameterName LIKE '%Enabled%' AND OverrideValue LIKE '%false%' ORDER BY OverrideLastModified DESC --End Query

image

 

Also – it can be nice to track your progress as you go.  If you are running it in a loop, it is nice to be able to see how many instances get deleted in each loop.  You can examine this by looking at the ManagedType tables in SCOM.  (MT_#####)  For example – here is a query that examines the number of DB Files, File Groups, and Log Files.  You might wish to disable discovery of these since they are no longer monitored in the current SQL MP.  You can refresh the query as you run the command.

--Begin Query SELECT SUM(TCount) As TotalCount FROM ( select count (*) as Tcount from MT_Microsoft$SQLServer$Windows$DBFile union all select count (*) as Tcount from MT_Microsoft$SQLServer$Windows$DBFilegroup union all select count (*) as Tcount from MT_Microsoft$SQLServer$Windows$DBLogFile ) as T --End Query

For a simpler query – you can also just count your total BaseManagedEntities, as these should shrink:

SELECT COUNT(*) AS 'BMEs' FROM BaseManagedEntity WHERE IsDeleted = 0

Some additional tips:

This command REQUIRES an override with Enabled = False to work.  If you simply change the default setting of a workflow to be disabled, this will not clean up previously discovered instances.  You must create an override with Enabled = False, even if the default configuration is Enabled = False for the discovery.

You should never be afraid to run this in any SCOM environment.  If you are worried about “what might be deleted” then your management group is a ticking time bomb.

7 Comments

  1. Bobgreen

    As I know SCOM Discovery Data Item has three options – Add (Snapshot = true), Update (Snapshot = false) and Delete. No one used third option but it should remove the object by Key properties.

  2. Kumaravelu

    Hi Kevin,

    Can we use the same Script in SCOM 2019 for scheduling the task.

    # Begin
    [Reflection.Assembly]::Load(“Microsoft.EnterpriseManagement.Core, Version=7.0.5000.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35”) | Out-Null
    [Reflection.Assembly]::Load(“Microsoft.EnterpriseManagement.OperationsManager, Version=7.0.5000.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35”) | Out-Null
    $mg = [Microsoft.EnterpriseManagement.ManagementGroup]::Connect(“localhost”)
    $mg.EntityObjects.DeleteDisabledObjects()

    regards,
    Kumar B

  3. Ravishankar

    Hi Kevin,

    Can you plese let me know what the below version and PublicKeyToken is referring in the below script.

    Version=7.0.5000.0
    PublicKeyToken=31bf3856ad364e35

    We want use this script in SCOM 2019, in that case version number and Token number will remain same ?

    # Begin
    [Reflection.Assembly]::Load(“Microsoft.EnterpriseManagement.Core, Version=7.0.5000.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35”) | Out-Null
    [Reflection.Assembly]::Load(“Microsoft.EnterpriseManagement.OperationsManager, Version=7.0.5000.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35”) | Out-Null
    $mg = [Microsoft.EnterpriseManagement.ManagementGroup]::Connect(“localhost”)
    $mg.EntityObjects.DeleteDisabledObjects()

    Please help me in calrifying my doubt.

    Regards,
    Ravi shankar

  4. Michael Smuda

    Kevin, just asking for the intension increasing the loop count at the beginning of the first run? Cause it will be increased at the end of the ‘while’ anyway.

    Write-Host “Loop number: $LoopCounter”
    IF ($LoopCounter -eq 1)
    {
    $LoopCounter++ # <—— this, i ment
    #First run always calls the delete command

  5. OdgeUK

    It was helpful to understand that Class Instances are only removed when there is an Override to the Discovery in place. We had a scenario where someone had written a custom class discovery that targeted Windows Server, and so had as many instances as there were Windows Server objects. Once we didn’t need this class anymore, I modified the MP to change the Discovery to Enabled = False and resealed it. I couldn’t understand why the classes wouldn’t clear using the SCOM-RemoveDisabledClassInstance. Now I have been able to do this, by putting a temporary override against the discovery, just to get the command to process the Class instances. In this way, it’s also possible to control the impact on your console / DB, where there are many objects to delete, by doing staged overrides to larger groups, then running the command, then repeating with another override. Tedious, but does control the churn.

Leave a Reply

Your email address will not be published.