Export and Import data of a MP in Service Manager when the MP needs to be removed as it cannot be upgraded (breaking change)

Ever authored a Management Pack and after it is in production for quite some time, you figured out that you need to make a breaking change - one that will require you to remove the MP first as it cannot be upgraded? Such a scenario would be needed if for some reason, you want to delete a Property of a Class for some reason.

But wait ... if we remove the MP, doesn't that mean that all the data in the database (instances & relationships) related to this MP, will be deleted? ... Yup, unfortunately it does ...

So, here is a PowerShell script that can be executed on a SM Management Server, which gets the (internal) name of the MP you need to remove and which will export (in memory) all the data (instances & relationships) related to the MP. It will then stop and wait for your key-press to continue.

Once it is waiting, you can delete the MP and re-import the updated version of the MP that has the change you made.

At this point, you can press any key in the PowerShell window where the script is waiting and it will start to create all instances & relationships that existed prior to removing the MP.

 

NOTE: Please be aware that I have tested only SOME scenarios. It might be that this will either fail, or not be able to re-create everything properly in who-knows-what-corner-case-scenarios. Please try to use and test this in pre-production if possible, and ALWAYS take a FRESH Full Backup of the ServiceManager database before attempting this in production.

 

 $mpName = "MPClassesHostingTest" # internal MP Name goes here
$message = "Perform the needed actions here (remove MP, re-import, etc.). After finishing the actions, press any key to continue."
$props = Get-ItemProperty "HKLM:\SOFTWARE\Microsoft\System Center\2010\Common\Setup"
$instdir = $props.InstallDirectory
Add-Type -Path "$instdir`SDK Binaries\Microsoft.EnterpriseManagement.Core.dll"
$dataMap = @{}
$emg = New-Object Microsoft.EnterpriseManagement.EnterpriseManagementGroup("localhost")
$mp = Get-SCManagementPack -Name $mpName
# sort (descending) by Hosted classes first so that when we re-create the objects, we start re-creating the Hosted objects first
# this is needed because Hosted objects need to be created together with their HostING objects
# this way, when we create non-Hosted objects, we skip creating HostING objects which we already have created when creating their Hosted objects
$classList = Get-SCClass -ManagementPack $mp | ? { $_.Singleton -eq $false -and $_.Abstract -eq $false } | Sort-Object Hosted -Descending
$hostingRel = Get-SCRelationship -Name "System.Hosting"
foreach($class in $classList) {
    $instanceList = Get-SCClassInstance -Class $class
    foreach($instance in $instanceList) {
        $object = @{
            instance = $instance;
            rels = @{
                source = Get-SCRelationshipInstance -SourceInstance $instance | ? { $_.IsDeleted -eq $false -and $_.Name -ne "System.Hosting" };
                target = Get-SCRelationshipInstance -TargetInstance $instance | ? { $_.IsDeleted -eq $false -and $_.Name -ne "System.Hosting" };
            };
        }
        $dataMap.Add($instance.EnterpriseManagementObject.Id, $object)
    }
}
if($psISE) {
    Add-Type -AssemblyName System.Windows.Forms
    [System.Windows.Forms.MessageBox]::Show("$message")
} else {
    Write-Host "$message" -ForegroundColor Yellow
    $x = $Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")
}
# Create all objects first so that we are sure we can use them for relationships afterwards
# These need to be all there created first in order (non-Hosted first & Hosted after)
foreach($key in $($dataMap.Keys)) {
    $entry = $dataMap.Item($key)
    $class = Get-SCClass -Name $entry.instance.EnterpriseManagementObject.GetClasses().Name
    $props = @{}
    if($class.Hosted -eq $true) {
        # need to handle Hosted classes differently (cannot use New-SCClassInstance cmdlet for various corner-cases)
        # handling with CreatableEnterpriseManagementObject instead (need to also create parent-Hosting class together with it)
        $hostingClass = $class.GetParentClasses([Microsoft.EnterpriseManagement.Configuration.DerivedClassTraversalDepth]::Recursive, $hostingRel, [Microsoft.EnterpriseManagement.Common.TraversalDepth]::OneLevel).Item(0)
        $hostingClassKey = $hostingClass.GetKeyProperties().Item(0)
        $object = New-Object -TypeName Microsoft.EnterpriseManagement.Common.CreatableEnterpriseManagementObject($emg, $hostingClass)
        $object.Item($hostingClass, $hostingClassKey.Name).Value = $entry.instance.$($hostingClassKey.Name)
        $object.Commit()
        $object = New-Object -TypeName Microsoft.EnterpriseManagement.Common.CreatableEnterpriseManagementObject($emg, $class)
        $object.Item($hostingClass, $hostingClassKey.Name).Value = $entry.instance.$($hostingClassKey.Name)
        foreach($property in $class.GetProperties([Microsoft.EnterpriseManagement.Configuration.BaseClassTraversalDepth]::Recursive)) {
            if($entry.instance.$($property.Name) -ne $null) {
                $object.Item($class, $property.Name).Value  = $entry.instance.$($property.Name)
            }
        }
        $object.Commit()
        $entry.instance = $object
    } elseif((Get-SCClassInstance -Id $entry.instance.EnterpriseManagementObject.Id) -eq $null) {
        foreach($property in $class.GetProperties([Microsoft.EnterpriseManagement.Configuration.BaseClassTraversalDepth]::Recursive)) {
            if($entry.instance.$($property.Name) -ne $null) {
                $props.Add($property.Name, $entry.instance.$($property.Name))
            }
        }
        $entry.instance = New-SCClassInstance -Class $class -Property $props -PassThru
    }
    $dataMap.Set_Item($key, $entry)
}
# Now that we know we have all objects created, we can safely create the Relationships
foreach($key in $dataMap.Keys) {
    $entry = $dataMap.Item($key)
    foreach($rel in $entry.rels.Item("source")) {
        $relType = Get-SCRelationship -Id $rel.RelationshipId
        $target = Get-SCClassInstance -Id $rel.TargetObject.Id
        if($target -ne $null) {
            New-SCRelationshipInstance -RelationshipClass $relType -Source $entry.instance -Target $target
        }
    }
    foreach($rel in $entry.rels.Item("target")) {
        $relType = Get-SCRelationship -Id $rel.RelationshipId
        $source = Get-SCClassInstance -Id $rel.SourceObject.Id
        if($source -ne $null) {
            New-SCRelationshipInstance -RelationshipClass $relType -Source $source -Target $entry.instance
        }
    }
}