As if often the case, this entire post would be pointless if the team implemented a small change:
Allow the Client Upgrade to be performed outside maintenance windows.  Go vote!

The product team has done a bang-up job of improving the upgrade process.  The most recent release at the time of this posting is Current Branch 1806 which appears to have been the smoothest one yet.  Windows product team … please take note.  One area that has seen major improvement is the client upgrade process.  While there’s a bunch of ways to upgrade the client I can’t think of any reason not to rely primarily on the Automatic Client Upgrade process built into the product.  You can pilot, you can exclude, and there’s some great reporting dashboards in the monitoring node.

Initially, the automatic client upgrade process apparently ignored maintenance windows.  Since the client upgrade deployment is hidden and unalterable there was really no way to control when it would happen.  Although upgrading the client should be one of the more unobtrusive thing you do I’m sure the U.S. Navy didn’t appreciate that lack of control on their nuclear subs.  In Current Branch 1511 they fixed this issue so that the client only upgrades during a valid maintenance window.  It’s hard to argue with that change but it creates an issue for one very specific yet very common use case.

“I Never Want Configuration Manager to Automatically Modify This Device”

This is a statement or requirement that many Configuration Manager administrators have had to face at one time or another.  Although it’s certainly worth fighting against there are both technical (complex patching orchestration) and political reasons that this is just the way it has to be.  The solution is dead-simple: create a non-repeating Maintenance Window that occurs in the past. Unless you specifically tell the deployment to ignore maintenance windows nothing will automatically install on devices in that collection.  Which brings us to …

The Problem: Upgrading Clients In a Maintenance Windows that Will Never Occur

Putting the above together creates a very specific issue for a very specific use case.  Devices that are in a maintenance window that is never going to occur are never going to have their client automatically upgraded.  The client upgrade deployment is hidden and unalterable so you can’t make it ignore maintenance windows.  I talked to a Premier Field Engineer and he concurred with this assessment and said that we would need to use one of the other deployment methods for this.  I’ve drunk the Application model kool-aid so that’s the route I went.  Which led to …

The Second Problem: I’m Apparently Really Bad at This

I made two huge mistakes when creating the application that I hope to save others from.  Let me tell you, this is not an application you want to screw up.  I both cases it led to infinite loops where the client would start up, attempt to update itself, and then start all over again.  Once set in motion there’s little you can do to stop this … the client isn’t alive long enough to get new policy before it starts the required install which quickly shuts the client down.  After a week or two of this you’re left with a smoldering heap on every client you targeted.  The only solution we found was to use some other method to reinstall the client: sneakernet.

Creating the Client Upgrade Application

There’s nothing particularly difficult or unique about creating a client upgrade application.  Grab the upgrade installer, run it with a command line along the lines of the one below, and create detection methods for it.  This sounds simple enough but I manged to bungle two out of those three.

CCMSETUP.EXE /noservice /skipprereq:silverlight.exe SMSSITECODE=%SITECODE%

Note that with the release Current Branch 1806 Silverlight is no longer needed or supported making that part of the command line unnecessary.

Fail #1: Where and When To Grab The Bits

The first thing I did wrong was where and when I grabbed the update installer.  When I tested this in the lab I had already promoted the new client to production and pointed the deployment type’s content to the ClientUpgrade folder listed below.  When my production counterpart did the same they hadn’t promoted the client yet.  The result: they deployed the old version’s upgrade installer but used a detection method looking for the new version.  The result: infinite installation loop.

To resolve this I looked closely at where the client upgrade installers exist on the primary site server.  I’m listing the UNC paths that can be used as content locations for a deployment type:

  • Production Client: \\%PrimarySiteServer%\SMS_%SiteCode%\ClientUpgrade
  • Pre-Production Client:  \\%PrimarySiteServer%\SMS_%SiteCode%\StagingClient

Note that instead of %PrimarySiteServer% you can just use localhost since it’s the primary server that is going to process those paths.  I recommend double checking that you have the correct version by looking at the installer’s details property tab.

Fail #2: Certainly CcmExec.exe’s Version Increments … Right?

The second thing I messed up was the detection method.  I needed some way of determining what version of the client was installed.  I mistakenly assumed that CcmExec.exe’s version would always match the client version.  The real mistake here was the failure to properly test and validate the application.  Not to defend the indefensible but in passing this can be misleading.  The client closes, updates to the latest version, and then starts back up. It … kinda … works. The problem is that it restarts that whole process in a few minutes.

The solution to this problem is obvious, use a detection method that works.  Having already broken one of my golden rules and been burned by it (I hate file version detection)  I looked through the registry more closely.  The only place I found the version string was where the client components are listed.  However, not every component version matches the client version and I wasn’t going to be bitten twice.  The only reliable way I found was to query WMI which means you need to use a script detection.  Something like this:

If ([System.Version](Get-WMIObject -NameSpace root\ccm -class SMS_Client -property ClientVersion).ClientVersion -ge [System.Version]'5.00.8498.1711'){
Write-Host 'Success'
}

So there you have it.  Please learn from my mistakes.  Oh, and go vote for these so we don’t have to do this at all or at least can deploy easily detectable MSPs:
Allow the Client Upgrade to be performed outside maintenance windows
Client Upgrade via Software Updates