Pete Hinchley: Using a Signed PowerShell Script within a Configuration Item in SCCM

If you are trying to create a configuration item in SCCM that uses PowerShell scripts for discovery and remediation, you might encounter an issue if your environment requires all scripts to be digitally signed. You could try configuring the PowerShell execution policy within the Computer Agent section of a Client Settings policy, but this approach won't work if the requirement for PowerShell scripts to be signed is enforced via group policy (as SCCM won't be able to override the group policy configuration).

Creating a discovery script is reasonably straight forward. You write the code, sign it, and then add it to the configuration item. The only catch is that you cannot paste the content of the signed script into the Edit Discovery Script dialog; instead, you must add the script using the Open button.

This is necessary, as the PowerShell script will be stored in the Configuration Manager database inside an XML document. According to the XML standard, new lines within an XML element must not contain carriage returns (i.e. Windows line-endings are not supported). Unfortunately, the signature block of a signed PowerShell script must contain Windows line-endings. Microsoft appear to have gotten around this limitation by storing a Base-64 encoded blob of the discovery script (along with the raw script) within the XML document. By encoding the script, the Windows line-endings can be retained.

When you use the “Open” button to add a discovery script to a configuration item in Configuration Manger, the Base-64 encoded blob is automatically generated. Unfortunately, this doesn’t work for the remediation script (this appears to be a bug in the current release of Configuration Manager - I've only tested on 1602). Fortunately, you can work around this issue by manually amending the remediation script to include a Base-64 encoded blob of itself.

Let's say you have created a signed remediation script named C:\Scripts\Remediate.ps1. You can append a Base-64 encoded copy of the script to itself using the following PowerShell code:

$script = "c:\scripts\remediate.ps1"

$starttag = "

# ENCODEDSCRIPT # Begin Configuration Manager encoded script block # "

$endtag = " # ENCODEDSCRIPT# End Configuration Manager encoded script block
"

$code = get-content -path $script -raw
$bytes = get-content -path $script -encoding byte -raw

$builder = new-object system.text.stringbuilder
$builder.append($code)
$builder.append($starttag)
$builder.append([convert]::tobase64string($bytes))
$builder.append($endtag)

$builder.tostring() | set-content $script

Having modified the remediation script, you can now paste it directly into the Edit Remediation Script dialog (or use the Open button if you prefer).

As a side note, you can inspect how the scripts are stored in the Configuration Manager database by using the Get-CMConfigurationItem cmdlet. For example, if the configuration item is named "Test Item", you can use the following code to dump the XML document containing the discovery script:

$name = "Test Item"
$xml = [xml](get-cmconfigurationitem -name $name).SDMPackageXML

$ns = new-object system.xml.xmlnamespacemanager($xml.nametable)
$ns.addnamespace("ns", $xml.documentelement.namespaceuri)
$node = $xml.desiredconfigurationdigest.selectsinglenode("//ns:DiscoveryScriptBody", $ns)

$node.innerxml

To extract the remediation script, just update the code to search for the RemediationScriptBody tag.