Going Deeper with ConfigMgr Server Group Patching



If you haven’t read our introduction blog into this feature check it out as this post is a continuation as we dig deeper into server group patching, leveraging the pre and post scripts as part of your more complex control for server patching. As of the 1802 release of Microsoft System Center Configuration Manager (ConfigMgr) the feature is still pre-release. Don’t hesitate to share your feedback through the Feedback Hub to help shape this feature.

As of the 1802 release there have been no published updates to this feature. However, with this post we dig deeper into how the pre/post scripts. Specifically, we’ll look at how Drain/Resume scripts work.

What else did we learn?

    • Script Timeout – This setting appears to be a suggestion only. When a script execution exceeds the timeout specified it does not timeout and return an error.
    • Script Errors – In our previous blog in our sample Drain/Resume scripts we used the Throw keyword to pass a failure back to the ConfigMgr client. However, it treats this as a success. You must use the Exit to pass a 0 for success, 3010 for a restart, and any other exit code for a failure. See below for updated sample scripts.
    • Script Download & Execution – As the script is a collection setting, it is downloaded through client policy and is downloaded from the Management Point (MP) and not as content from a Distribution Point (DP). When the script is run it is saved to the C:\Windows\Temp\CCMXXX.tmp.ps1 and executed.
    • Software Center – If your administrators attempt to “Install all” updates for a system that is waiting for a lock, they will get the following obtuse message:

Under the covers in the UpdateDeployments.log you will see the following error (0x87d00666 -Software updates cannot be installed outside service window):

Essentially the updates cannot be installed until a lock is available, and Software Center is not going to tell you.

    • Install All Updates via Software Center – When you choose the Install All updates in software center on a server that has an available lock (either first server in a sequence, or the number/percentage cap hasn’t been reached) then it will install all the updates. Unfortunately, the way this was implemented by Microsoft, it will run the Drain script immediately in succession for each applicable update. If you have eight required updates, it will run the script eight times rapidly in succession before it starts patching.

To address this, you should implement a flag file with a timestamp to prevent the Drain script from running over-and-over in rapid succession. Something to the following would work:

$f = "$env:TEMP\patch.flg"
#// Test for flag file to see if recently ran
If (Test-Path $f) {
    $fl = gci $f
    If ($fl.LastWriteTime -gt (Get-Date).AddMinutes(-1)) {
        Write-Host "Recently run"
        Exit 0
Write-Host "Haven't run recently"
#// Create Flag
$Null | Out-File $f -Force

The Write-Host in the sample script are not necessary. They are added when testing to highlight the functionality.

PowerShell Node Drain and Resume Script

In the previous blog we provided sample Drain/Resume scripts which called a script from a network share to work around the 512 character limit. The revised sample script is revised for better error handling and ensuring that we return a failure exit code that can be interpreted by the ConfigMgr client.

The revised script can be used for both the Drain/Resume Node script, simply change out the $m variable to specify the mode to run the parent script. Your Patch.ps1 script will need to have a mode parameter and take the appropriate actions for the server for either the drain or resume mode.

Replace the SERVER place holder with the name for your server. The below example script is exactly 502 characters, so if your server name adds any more than 10 characters you will need to trim the script down.

The biggest improvement in this version is that it wraps your network script in a try catch, and appends the output to the Patch.txt in the temp folder and returns a failure exit code to the ConfigMgr client.

$l = 'C:\windows\temp\patch.txt'
$m = 'DRAIN'
"$m|Start $(Get-Date)"|Out-File $l -append
$s = '\\SERVER\patch\Patch.ps1'
If (Test-Path ($s)) {
   $c = "$s"+" -mode '$m'"
   Try {
        Invoke-Expression $c
    } Catch {
        $e = $_.Exception.HResult
        "ERROR [$e] $($_.Exception.Message)($($_.InvocationInfo.ScriptLineNumber)"|Out-File $l -append
        Exit $e
} Else {
    "Not found $s"|Out-File $l -append
    Exit 404
"$m|Stop $(Get-Date)"|Out-File $l -append


Matt Tinney
CEO, Windows Management Experts

Get free education resources and more at the Adaptiva Academy

Get Free Stuff