Export / Backup Compliance Setting Scripts with PowerShell

In my SCCM environment I have a number of Compliance Settings that use custom scripts for discovery and remediation, and recently it dawned on me that a lot of time has been spent on these and it would be good to create a backup of those scripts. It would also be useful to be able to export the scripts so they could be edited and tested before being updated in the Configuration Item itself. So I put to together this PowerShell script which does just that!

The Configuration Item scripts are stored in an XML definition, and this can be read from the SCCM database directly and parsed with PowerShell, so that’s what this script does. It will load all the Configuration Items into a datatable from a SQL query, then go through each one looking for any settings that have scripts defined. These scripts will be exported in their native file format.

You could then edit those scripts, or add the export location to your file/folder backup for an extra level of protection for your hard work!

Here you can see an example of the output for my “Java Settings” Configuration item. A subdirectory is created for the current package version, then subdirectories under that for each Configuration setting, then the discovery and remediation scripts for that setting.

cis
Exported Configuration Item Scripts

Note that the script will only process Compliance Items with a CIType_ID of 3, which equates to the Operating System type you will see in the SCCM console for the Configuration Item, which is the type that may use a script as the discovery source.

Export-CMConfigurationItemScripts.ps1


<#
.Synopsis
Exports all scripts (discovery and remediation) used in all SCCM Compliance Setting Configuration Items
.DESCRIPTION
This script connects to the SCCM database to retrieve all Compliance Setting Configuration Items. It then processes each item looking for
discovery and remediation scripts for the current (latest) version. It will export any script found into a directory structure.
.NOTES
Requirements – 'db_datareader' permission to the SCCM SQL database with the account running this script.
Parameters – set the parameters below as required
#>
################
## PARAMETERS ##
################
# Root directory to export the scripts to
$RootDirectory = "C:\temp"
# Name of the subdirectory to create
$SubDirectory = "Compliance_Settings_CI_Scripts"
# SCCM SQL Server (and instance where applicable)
$SQLServer = 'mysqlserver\inst_sccm'
# SCCM Database name
$Database = 'CM_ABC'
##################
## SCRIPT START ##
##################
# Create the subdirectory if doesn't exist
If (!(Test-Path "$RootDirectory\$SubDirectory"))
{
New-Item Path "$RootDirectory" Name "$SubDirectory" ItemType container | Out-Null
}
# Define the SQL query
$Query = "
Select * from dbo.v_ConfigurationItems
where CIType_ID = 3
and IsLatest = 'true'"
# Run the SQL query
$connectionString = "Server=$SQLServer;Database=$Database;Integrated Security=SSPI"
$connection = New-Object TypeName System.Data.SqlClient.SqlConnection
$connection.ConnectionString = $connectionString
$connection.Open()
$command = $connection.CreateCommand()
$command.CommandText = $Query
$reader = $command.ExecuteReader()
$ComplianceItems = New-Object TypeName 'System.Data.DataTable'
$ComplianceItems.Load($reader)
$connection.Close()
# Process each compliance item returned
$ComplianceItems | foreach {
# Set some variables
$PackageVersion = "v $($_.SDMPackageVersion)"
[xml]$Digest = $_.SDMPackageDigest
$CIName = $Digest.ChildNodes.OperatingSystem.Annotation.DisplayName.Text
# Create subdirectory structure if doesn't exist: configuration item name > current package version
If (!(Test-Path "$RootDirectory\$SubDirectory\$CIName"))
{
New-Item Path "$RootDirectory\$SubDirectory" Name "$CIName" ItemType container | Out-Null
}
If (!(Test-Path "$RootDirectory\$SubDirectory\$CIName\$PackageVersion"))
{
New-Item Path "$RootDirectory\$SubDirectory\$CIName" Name "$PackageVersion" ItemType container | Out-Null
}
# Put each compliance item setting in XML format into an arraylist for quick processing
$Settings = New-Object System.Collections.ArrayList
$Digest.DesiredConfigurationDigest.OperatingSystem.Settings.RootComplexSetting.SimpleSetting | foreach {
[void]$Settings.Add([xml]$_.OuterXml)
}
# Process each compliance item setting
$Settings | foreach {
# Only process if this setting has a script source
If ($_.SimpleSetting.ScriptDiscoverySource)
{
# Set some variables
$SettingName = $_.SimpleSetting.Annotation.DisplayName.Text
$DiscoveryScriptType = $_.SimpleSetting.ScriptDiscoverySource.DiscoveryScriptBody.ScriptType
$DiscoveryScript = $_.SimpleSetting.ScriptDiscoverySource.DiscoveryScriptBody.'#text'
$RemediationScriptType = $_.SimpleSetting.ScriptDiscoverySource.RemediationScriptBody.ScriptType
$RemediationScript = $_.SimpleSetting.ScriptDiscoverySource.RemediationScriptBody.'#text'
# Create the subdirectory for this setting if doesn't exist
If (!(Test-Path "$RootDirectory\$SubDirectory\$CIName\$PackageVersion\$SettingName"))
{
New-Item "$RootDirectory\$SubDirectory\$CIName\$PackageVersion" Name $SettingName ItemType container Force | Out-Null
}
# If a discovery script is found
If ($DiscoveryScript)
{
# Set the file extension based on the script type
Switch ($DiscoveryScriptType)
{
Powershell { $Extension = "ps1" }
JScript { $Extension = "js" }
VBScript { $Extension = "vbs" }
}
# Export the script to a file
New-Item Path "$RootDirectory\$SubDirectory\$CIName\$PackageVersion\$SettingName" Name "Discovery.$Extension" ItemType file Value $DiscoveryScript Force | Out-Null
}
# If a remediation script is found
If ($RemediationScript)
{
# Set the file extension based on the script type
Switch ($RemediationScriptType)
{
Powershell { $Extension = "ps1" }
JScript { $Extension = "js" }
VBScript { $Extension = "vbs" }
}
# Export the script to a file
New-Item Path "$RootDirectory\$SubDirectory\$CIName\$PackageVersion\$SettingName" Name "Remediation.$Extension" ItemType file Value $RemediationScript Force | Out-Null
}
}
}
}
<# For reference: CIType_IDs
1 Software Updates
2 Baseline
3 OS
4 General
5 Application
6 Driver
7 Uninterpreted
8 Software Updates Bundle
9 Update List
10 Application Model
11 Global Settings
13 Global Expression
14 Supported Platform
21 Deployment Type
24 Intend Install Policy
25 DeploymentTechnology
26 HostingTechnology
27 InstallerTechnology
28 AbstractConfigurationItem
60 Virtual Environment
#>

4 thoughts on “Export / Backup Compliance Setting Scripts with PowerShell

  1. I had to include a Function to remove Invalid Chars in the CI Name and the Settings Name.
    If not, the folders coulnd’t be created for some CIs

    $CIName = Remove-InvalidFileNameChars $CIName
    $SettingName = Remove-InvalidFileNameChars $SettingName

    Function Remove-InvalidFileNameChars {
    param(
    [Parameter(Mandatory=$true,
    Position=0,
    ValueFromPipeline=$true,
    ValueFromPipelineByPropertyName=$true)]
    [String]$Name
    )

    $invalidChars = [IO.Path]::GetInvalidFileNameChars() -join ”
    $re = “[{0}]” -f [RegEx]::Escape($invalidChars)
    return ($Name -replace $re)
    }

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.