During a disaster recover scenario, it would be nice to know that all of your custom data is backed up as much as possible. While there are a few things within Service Manager that do not get saved to management packs, most items do. If you have custom management packs in Service Manager, you can rest assured that you will always have a backup with the latest changes by following this post.

The solution we have written doesn’t just backup your management packs, but also compares the last modified date of your MPs to the last write time of your most recent backup and exports the MP only if there are changes. With this logic you can reduce load on Service Manager and disk reads/writes if your MPs don’t often see many changes.

Requirements

This PowerShell script needs some way to identify your custom management packs. We include our team name in the name of all of our custom management packs so we can quickly identify which management packs are ours and which ship with the product. When running the script, this is specified with the “MP” switch.

If you need to be able to export management packs that have multiple naming conventions, you will need to create a text file containing the exact “internal” names of all the management packs you want exported and specify the full path to this text file when running the script using the “List” switch.

Running the script

The PowerShell script is at the end of this post under the heading “The Script”. Simply copy and paste it into a text file and save the file as “ExportMP.ps1”. Make sure you choose “All Files (*.*)” under ‘Save as type’ so the .txt extension isn’t automatically added to the end of the file.

clip_image002

The PowerShell script is run as follows:

.\ExportMP.ps1 -path c:\mypath -mp mypartialmpname -computer smmaangementserver.contoso.com -list c:\mp.txt

Where:

  • Path is the path your management packs will be saved to (defaults to c:\windows\temp if not specified). This should be a remote location outside of your Service Manager management servers.
  • Mp is the unique identifier used to search for your custom management packs
    • The script will do a wildcard search for MPs containing what you specify, e.g., “where name -like *$MP*”
  • Computer is the name of one of the Service Manager management servers (defaults to “localhost” if not specified)
  • List is the full path to a text file containing a list of servers if MP is not specified
    • If both list and MP are specified, the list is used and not “MP”

If you aren’t interested in seeing how the script works, you can skip to the “Creating a scheduled task” section near the bottom of this post. Otherwise, the next section goes through how the script works. I break the script down in case it helps someone learn something new.

Dissecting the script

The script is fairly straight forward, however it gets complicated after taking into consideration the variables that different SM environments might have, as well as documenting each step of the script and adding some error handling.

The base script started off with some hardcoded values, minimal notes, and no error handling and came in at 11 lines long:

If ( ! (Get-module System.Center.Service.Manager )) { Import-Module "$env:programfiles\Microsoft System Center 2012\Service Manager\Powershell\System.Center.Service.Manager.psd1" }

$MP = get-scmanagementpack -computer MPSDSM12 | where name -like mpsd*

$Path = "\\co1msftvmmutil1\monmang\scsm\Management Packs\2012"

Foreach ($name in $MP)

{$XMLName = $name.name + ".xml"

$MPName = $name.name

$dir = get-childitem $Path $XMLName

if (($name.Lastmodified).ToLocalTime() -gt $dir.lastwritetime)

{if ($dir) {move-item -path $Path\$XMLName -destination $Path\OLD\ -force}; export-scmanagementpack $Name -path $Path; write-host "MP $MPName has been updated!" -fore yellow} else {write-host "MP $MPName up to date!" -fore green}}

After making it more user friendly, our total length is 90 lines.

Taking apart the full script, after defining some parameters and setting defaults, the first thing we do is to clear the error variable and make it so no errors are displayed, as we will be handling errors ourselves:

 

#Clear error variable for better error handling

$error.clear()

 

#Set error action preference to hidden to not confuse users and handle errors ourselves

$ErrorActionPreference = "SilentlyContinue"

 

If neither the -mp nor the -list switch is defined, the script will export all MPs in the system. If we hit this scenario we want to warn the end-user that they are about to export all MPs and prompt them to continue or cancel.

 

#If -mp or -list switch not supplied, warn user

If ((! $MP) -and (!$List))

{

       $object = new-object -comobject wscript.shell

       $intAnswer = $object.popup("You have not specified a management pack search string or list of management packs.

 

       This will export all management packs from Service Manager!

 

Continue?",0,"Export all MPs?",4)

       If ($intAnswer -ne 6)

       {

              write-host Please include the `-mp or `-list switch. -fore yellow; exit

       }

}

 

As with a lot of my scripts, we rely on the built-in Service Manager cmdlets. We therefore need to import the System.Center.Service.Manager module if not already imported. We also like to keep the user aware of the progress (useful if running manually and not in a scheduled task):

#Get MPs containing MPSD and export if last modified date is more recent than last export:

If ( ! (Get-module System.Center.Service.Manager ))

{

       write-host Loading Service Manager module

       Import-Module "$env:programfiles\Microsoft System Center 2012\Service Manager\Powershell\System.Center.Service.Manager.psd1"

}

 

Next we need to build a list of management packs to query the last modified time from. If the -mp switch was defined, we will do a wildcard search of the MP name on the value specified. If the -list switch was defined, we will query for management pack names found in the list. If both are specified, we will use the list.

#Get MPs using the string from the MP switch if list not specified

If(! $List)

{

       $FullMP = get-scmanagementpack -computer $computer | where name -like *$MP*

       write-host Getting all MPs that contain `"$MP`"

       write-host "Comparing last modified time of files in $Path to all `"$MP`" MPs"

}

 

#Else get MPs from list

Else

{

       $list1 = get-content $List

       $FullMP = get-scmanagementpack -computer $computer | where name -in $List1

       write-host Comparing last modified time to files in $Path to all MPs listed in $List

}

 

Since we have just now queried Service Manager for the first time, we can now error if the Service Manager server was incorrectly defined:

#Error if invalid SDK server specified

If ($error[0] -like "The Data Access service is either not running or not yet initialized*")

{

       write-host Invalid management server specified. Please verify the `"computer`" switch and try again. -fore red -back black; exit

}

 

We will also error if no MPs were returned:

#Error if no MPs returned

If (! $FullMP)

{

       write-host No management packs found.  Please verify search string in the `"MP`" switch or list of management packs in your list are correct and try again. -fore red -back black; exit

}

Once we have a list of MPs from Service Manager, we can actually start the comparison. There are a few things going on here.

  • First we use the foreach loop so we can look individually at each MP that was returned.
  • For each MP, we add “.xml” to the end since that’s how the MPs are named when exported.
  • We set $dir and $old to see if there are existing management packs that are in the directory we are saving to, as well as if we already have an “old” folder.
  • We compare the last modified time that Service Manager has (converted to local time) to the last write time of the files defined in $path. If there are MPs that need exported, we prepare the directory for saving them:
    • If there are already MPs saved, we create an “old” folder (if not already present) and move those MPs to the folder in preparation of saving the MPs.
      • If no MPs we skip this
  • Finally, we export the MPs to the given directory and output a message indicating this.

#Do the work

Foreach ($name in $FullMP)

{

       #Add .xml to end of the MP name retrieved from SM

       $XMLName = $name.name + ".xml"

       #Set MPname for use in keeping end user up to date on status

       $MPName = $name.name

       #Set "dir" variable so we can verify if the management packs really exist

       $dir = get-childitem $Path $XMLName

       #set "old" variable so we can verify if the old folder exists and create if necessay

       $old = get-childitem $path old

       #If the MP from SM is newer than what we have in our directory, create an "old" folder and move existing MPs to that folder first, otherwise just export

       if (($name.Lastmodified).ToLocalTime() -gt $dir.lastwritetime)

       {

              if ($dir)

              {

                     if (! $old)

                     {

                           new-item -path $path -name old -itemtype directory

                     }

                     move-item -path $Path\$XMLName -destination $Path\OLD\ -force

                     write-host Backing up existing files that will be updated to $Path\Old

              }

                     export-scmanagementpack $Name -path $Path

                     write-host "MP $MPName has been updated!" -fore yellow

       }

 

If the MPs in $path are already up to date, the script outputs a message for each MP stating that the MP is already up-to-date.

       #If all is up to date, just indicate everything is good

       else

       {

              write-host "MP $MPName up to date!" -fore green

       }

}

 

Creating a scheduled task

While the script can be run manually, ideally you don’t want to worry about running the script manually at set intervals. Here we will create a scheduled task that runs once every 24 hours (ideally after all MP changes are complete for the day).

While the scheduled task can really be run from any server (it doesn’t even have to be a Service Manager management server) I like to keep all Service Manager components together so I place this script on the primary management server. Make sure you also place your text file containing your list of management packs on the computer as well.

To start, we will create a task on the computer running the script. After giving it a meaningful name, set a new trigger to run daily with a start time that will generally be after all MP changes are made for the day.

clip_image003

Under the ‘actions’ tab click ‘New’ and choose “Start a program” in the ‘Action’ drop-down. In the ‘Program/script’ textbox, we will call “powershell.exe”. In the ‘Add arguments’ text box, we will call the PowerShell script, setting the switches as necessary:

-NoLogo -NonInteractive -File C:\ExportMP.ps1 -path “C:\MP Backups” -MP MPSD

clip_image004

Once you click OK, on the general tab choose the radio button next to ‘Run whether user is logged on or not’ and set the user to someone that has admin permissions (or just use the service account that Service Manager is running under), then click OK (entering a password for the account chosen if prompted).

You can test to make sure the task works by right-clicking the task and choosing “Enable”, then verify your MPs were saved to the output specified.

The Script

Copy and paste this into a text file and save the file as “ExportMP.ps1”. Make sure you choose “All Files (*.*)” under ‘Save as type’ so the .txt extension isn’t automatically added to the end of the file.

param ($path="C:\windows\temp", $MP, $computer = "localhost", $List)
 
#Clear error variable for better error handling
$error.clear()
 
#Set error action preference to hidden to not confuse users and handle errors ourselves
$ErrorActionPreference = "SilentlyContinue"
 
#If -mp or -list switch not supplied, warn user
If ((! $MP) -and (!$List))
{
    $object = new-object -comobject wscript.shell 
    $intAnswer = $object.popup("You have not specified a management pack search string or list of management packs.
 
    This will export all management packs from Service Manager!
 
Continue?",0,"Export all MPs?",4)
    If ($intAnswer -ne 6) 
    {
        write-host Please include the `-mp or `-list switch. -fore yellow; exit
    }
}
 
#Import SM module if not present
If ( ! (Get-module System.Center.Service.Manager )) 
{
    write-host Loading Service Manager module
    Import-Module "$env:programfiles\Microsoft System Center 2012\Service Manager\Powershell\System.Center.Service.Manager.psd1"
}
 
#Get MPs using the string from the MP switch if list not specified
If(! $List)
{
    $FullMP = get-scmanagementpack -computer $computer | where name -like *$MP*
    write-host Getting all MPs that contain `"$MP`"
    write-host "Comparing last modified time of files in $Path to all `"$MP`" MPs"
}
 
#Else get MPs from list
Else
{
    $list1 = get-content $List
    $FullMP = get-scmanagementpack -computer $computer | where name -in $List1
    write-host Comparing last modified time to files in $Path to all MPs listed in $List
}
 
#Error if invalid SDK server specified
If ($error[0] -like "The Data Access service is either not running or not yet initialized*")
{
    write-host Invalid management server specified. Please verify the `"computer`" switch and try again. -fore red -back black; exit
}
 
#Error if no MPs returned
If (! $FullMP)
{
    write-host No management packs found.  Please verify search string in the `"MP`" switch or list of management packs in your list are correct and try again. -fore red -back black; exit
}
 
#Do the work
Foreach ($name in $FullMP)
{
    #Add .xml to end of the MP name retrieved from SM
    $XMLName = $name.name + ".xml"
    #Set MPname for use in keeping end user up to date on status
    $MPName = $name.name
    #Set "dir" variable so we can verify if the management packs really exist
    $dir = get-childitem $Path $XMLName
    #set "old" variable so we can verify if the old folder exists and create if necessay
    $old = get-childitem $path old
    #If the MP from SM is newer than what we have in our directory, create an "old" folder and move existing MPs to that folder first, otherwise just export
    if (($name.Lastmodified).ToLocalTime() -gt $dir.lastwritetime)
    {
        if ($dir) 
        {
            if (! $old)
            {
                new-item -path $path -name old -itemtype directory
            }
            move-item -path $Path\$XMLName -destination $Path\OLD\ -force
            write-host Backing up existing files that will be updated to $Path\Old
        }
            export-scmanagementpack $Name -path $Path
            write-host "MP $MPName has been updated!" -fore yellow
    }
    #If all is up to date, just indicate everything is good
    else
    {
        write-host "MP $MPName up to date!" -fore green
    }
}