ConfigMgr Export/Import Hardware Inventory Classes

Here is something that I have run into a few times now so I created a script to help make all our lives a bit easier.

When applying updates to the ConfigMgr environment, modifications to the Hardware Inventory classes in the default and custom client settings sometimes get reset to the default.  For some people and environments, this might not matter too much.  However, if you have heavily customized Hardware Inventory Class settings or many custom client settings that you might want to preserve, this can be an annoyance.

I decided the create a PowerShell script that will export all the current Hardware Inventory Class settings from the default and custom client settings into an XML file.  The script can also then use the same XML file to import the setting back (post update).

You can find the full script here.

 

What I will do here, like I typically do, it break the script down for you as a learning exercise.

The first part of the script just sets up our variables to be used:

 [cmdletbinding()]
param(
    [parameter(Mandatory=$true)]
    [ValidatePattern('.*(.xml)')]
    [String]
    $XMLFile,

    [switch]
    $Import,

    [parameter(Mandatory=$true)]
    [string]
    $SiteCode,

    [parameter(Mandatory=$true)]
    [string]
    $CMProvider = "."
)

$CIMProps = @{
    ComputerName = $CMProvider
    Namespace = "root\sms\site_$($SiteCode)"
}

$TodaysDate = get-date -Format "yyyy-MM-dd-HHmmss"

We have defined 4 parameters that will be used in the script; XMLFile, Import, SiteCode, CMProvider.

The SiteCode and CMProvider are required for the script to know what server hosts the CMProvider and the SiteCode is required to know the correct WMI Namespace to connect to.

The XMLFile is the name of the file we will be exporting the settings to or importing the settings from. This parameter has a Regular Expression ValidatePattern to ensure the file name ends with .XML.  This is important in this case because we later do a "split" on the file name at the .xml part to include today's date for some version control.

The Import switch parameter we use at the end to determine what "mode" we are running in.

 

The next part of the script is the ExportClientHardwareInventoryClasses scriptblock.  This is the part that actually connects to the ConfigMgr Provider, queries for the client settings, and saves the information to an XML File.

 $ExportClientHardwareInventoryClasses = {

We start off by splitting the XMLFile name and adding today's date to it and setting up an array (ExportHWInvClasses) to store our results.

     $File = $XMLFile -split '.xml'
    $File = "$($XMLFile)-$($TodaysDate).xml"
    $ExportHWInvClasses = @()    

Next, we connect to the ConfigMgr provider using the values stored in the $CIMProps hashtable using the splat (@) operator.  We query the SMS_ClientSettings WMI class and pipe again to Get-CIMInstance in order to retrieve the lazy properties.  This class contains the custom client settings.

     $ClientSettings = Get-CimInstance @CIMProps -ClassName SMS_ClientSettings | Get-CimInstance

Next, we query the SMS_ClientSettingsDefault class as this class contains the default client settings.  We have to do a little data manipulation of the returned object to make it "match" the same as the custom client settings.  The default hardware inventory report id is well known so we create a new property on our resulting object and add the report id.  The Hardware Inventory Agent ID is 15, so we add that value as well since we are filtering on it later.  We then add this to the array of client settings.

     $DefaultClientSettings =  Get-CimInstance @CIMProps -ClassName SMS_ClientSettingsDefault | Get-CimInstance
    $DefaultClientSettings | Add-Member -NotePropertyName AgentConfigurations -NotePropertyValue @{}
    $DefaultClientSettings.AgentConfigurations.Add('AgentID',15)
    $DefaultClientSettings.AgentConfigurations.Add('InventoryReportID','{00000000-0000-0000-0000-000000000001}')
    $ClientSettings += $DefaultClientSettings

The next step is to loop through the clients settings that we have found, extract the InventoryReportID value. We create a hashtable to store the data in, noting the ClientSetting, AgentConfigurations, and the InventoryReport.  The InventoryReport data is gathered by querying the SMS_InventoryReport class with the InventoryReportID to get the details about which inventory classes and properties are enabled for collection.  These are lazy properties so we have to pipe to Get-CimInstance again to get the details.

     Foreach ($ClientSetting in $ClientSettings) {
        if ($HWInvSetting = $ClientSetting.AgentConfigurations | where {$_.AgentID -eq 15}) {
            write-verbose $HWInvSetting.InventoryReportID 
            $HWInvDetails = @{
                ClientSetting = $ClientSetting
                AgentConfiguration = $HWInvSetting
                InventoryReport =  Get-CimInstance @CIMProps -ClassName SMS_InventoryReport -Filter ('InventoryReportID = "' + $HWInvSetting.InventoryReportID + '"') | Get-CimInstance
            }
            $ExportHWInvClasses += $HWInvDetails
        }
    }

Then we export the results to an the XML File

     Export-Clixml -InputObject $ExportHWInvClasses -Path $File
}

The next section is the scriptblock that is used to Import Client Hardware Inventory Classes.  If takes the XMLFile as an input to read in the data that was stored.

 $ImportClientHardwareInventoryClasses = {
    $ImportedHWInvClasses = Import-Clixml $XMLFile

Then we loop through the resulting object, connect to WMI, and retrieve the matching Inventory Report, set the ReportClasses on the Inventory Report to those from the XML file, and then set that object back in WMI.  This results in updating the Hardware Inventory Classes/Properties on each Client Settings that was exported.

     foreach ($ClientSetting in $ImportedHWInvClasses) {
        $InventoryReport = Get-CimInstance @CIMProps -ClassName SMS_InventoryReport -Filter ('InventoryReportID = "'+ $ClientSetting.InventoryReport.InventoryReportID + '"') | Get-CimInstance
        $InventoryReport.ReportClasses = $ClientSetting.InventoryReport.ReportClasses
        $InventoryReport | Set-CimInstance
    }
}

Finally, we have the control portion of the script which determines which script block gets executed, the import or export.  This is based on the switch parameter provided to the script.  If -import is not added to the parameters when run, the script is running in export mode.  With -import entered, the script runs in import mode.

 If ($Import) {
    Invoke-Command $ImportClientHardwareInventoryClasses
} else {
    Invoke-Command $ExportClientHardwareInventoryClasses
}

 

Let me know what your thoughts are!

 


As noted below, all code is provided for sample use only.

Microsoft provides programming examples for illustration only, without warranty either expressed or implied, including, but not limited to, the implied warranties of merchantability and/or fitness for a particular purpose.

This sample assumes that you are familiar with the programming language being demonstrated and the tools used to create and debug procedures. Microsoft support professionals can help explain the functionality of a particular procedure, but they will not modify these examples to provide added functionality or construct procedures to meet your specific needs. If you have limited programming experience, you may want to contact a Microsoft Certified Partner or the Microsoft fee-based consulting line at (800) 936-5200.

For more information about Microsoft Certified Partners, please visit the following Microsoft Web site: https://partner.microsoft.com/global/30000104