Since the release of ConfigMgr Current Branch (version 1511 and onward), the experience of updating ConfigMgr to the latest version has improved in between versions and with 1606 out, it’s better than ever. However, there’s a major piece missing that we’re still not able to perform just yet, which is to invoke an update of ConfigMgr through PowerShell. I’d assume that this is coming in a future release of the Configuration Manager Cmdlets Library, but we’re not there quite yet. In order to get around this, I’ve written a PowerShell script that is able to determine if there’s an update available for ConfigMgr, based on specified version input.
Ryan Ephing recently published a method where he triggers the UpdatePrereqAndStateFlags method on an instance from the SMS_CM_UpdatePackages class (great post Ryan!). I’ve been working on the same method, but added a few more verification steps in order to prevent unnecessary restarts of the SMS_EXECUTIVE service (which sometimes are required when the update package is stuck in the Downloading state, shown in the Updates and Servicing node of the ConfigMgr console).
Documentation
Since this script is leveraging undocumented classes and methods in the SMS Provider, I’d urge you to use it with caution. Never run this script in your production environment before testing it in a lab environment first. Using the script is quite simply, where you specify your Primary Site server for the SiteServer parameter, a 4 characters long string for the Version parameter (1602 or 1606 for instance). That’s all you have to do basically. However, I’ve added some logic to enter a loop if the script detects that the Update Package found is either being downloaded or that Prerequisites Checks are being executed. To control the loop for these two scenarios, the script has two additional parameters that are not required (and have a default value of 120):
- AvailabilityCheckCount
- PrerequisiteCheckCount
By specifying for instance the AvailibilityCheckCount parameter with a value of 60, when the script enters the waiting for Update Package to become Available loop (it downloads before it becomes available). It will performs a sleep operation for 30 seconds up until the count hits what’s specified in AvailibilityCheckCount. When the loop hits 60, it will perform a restart of the SMS_EXECUTIVE service, hopefully bringing the Update Package out of the Downloading state, let it download if it hasn’t yet. The script will then continue when the state has changed to Available.
The same logic applies for the PrerequisitesCheckCount parameter, that is used in a loop to determine when the state has changed to Installing from Running Prerequisites Checks. Before this loop is entered though, the script has trigged the available Update Package to be installed.
In my lab environment, I have access to a rather fast pipe and therefor the download of the EasySetupPayload (Update Package, e.g. 1606) doesn’t take that much time (about a minute or so). This means that using the default counts for both the AvailibilityCheckCount and PrerequisitesCheckCount makes little sense. However, in an environment where the download of an Update Package may take some time (let’s say more than 1 hour), I’d advice that you don’t use the default values of these two parameters, and instead set your own values that suits your environment.
NOTE! Always perform the update and servicing of ConfigMgr from your top site server in your hierarchy. This applies when running this script as well.
Script
You can grab the script from below and save it as e.g. Invoke-CMUpdatePackage.ps1. I’ve also uploaded it to my GitHub repository, if you want to grab it from there instead.
<# .SYNOPSIS Start installation of a Configuration Manager Update Package (servicing model for Current Branch) .DESCRIPTION This script will attempt to start the installation of a specific version of Configuration Manager Current Branch. If an Update Package is available for the specified version of Configuration Manager, the Update Package will be validated for availability before any installation is started. The script supports restarting SMS_EXECUTIVE in the event that the Update Package is stuck in the Downloading state. You can control the amount of availibility checks to be performed with the AvailibilityCheckCount parameter, before the service is restarted. Default is 120. .PARAMETER SiteServer Site server name with SMS Provider installed. .PARAMETER Version Specify a Configuration Manager version that should be installed. Valid format is e.g. 1602 or 1606. .PARAMETER AvailabilityCheckCount Specify how many times the script will check if an UpdatePackage is available for installation. .PARAMETER PrereqCheckCount Specify how many times the script will check if the prerequisite checks has completed for an UpdatePackage. .EXAMPLE Attempt to start the installation of Configuration Manager 1606, on a Primary Site server called 'CM01': .\Invoke-CMUpdatePackage.ps1 -SiteServer CM01 -Version 1606 .NOTES FileName: Invoke-CMUpdatePackage.ps1 Author: Nickolaj Andersen Contact: @NickolajA Created: 2016-08-06 Updated: 2016-08-06 Version: 1.0.0 #> [CmdletBinding(SupportsShouldProcess=$true)] param(
[parameter(Mandatory=$true, HelpMessage=”Site server where the SMS Provider is installed.”)]
[ValidateNotNullOrEmpty()] [ValidateScript({Test-Connection -ComputerName $_ -Count 1 -Quiet})] [string]$SiteServer,
[parameter(Mandatory=$true, HelpMessage=”Specify a Configuration Manager version that should be installed. Valid format is e.g. 1602 or 1606.”)]
[ValidateNotNullOrEmpty()] [ValidateScript({$_.Length -eq 4})] [string]$Version,
[parameter(Mandatory=$false, HelpMessage=”Specify how many times the script will check if an UpdatePackage is available for installation.”)]
[ValidateNotNullOrEmpty()] [int]$AvailabilityCheckCount = 120,
[parameter(Mandatory=$false, HelpMessage=”Specify how many times the script will check if the prerequisite checks has completed for an UpdatePackage.”)]
[ValidateNotNullOrEmpty()] [int]$PrerequisiteCheckCount = 120 ) Begin { # Determine SiteCode from WMI try { Write-Verbose -Message “Determining Site Code for Site server: ‘$($SiteServer)'” $SiteCodeObjects = Get-WmiObject -Namespace “root\SMS” -Class SMS_ProviderLocation -ComputerName $SiteServer -ErrorAction Stop foreach ($SiteCodeObject in $SiteCodeObjects) { if ($SiteCodeObject.ProviderForLocalSite -eq $true) { $SiteCode = $SiteCodeObject.SiteCode Write-Verbose -Message “Site Code: $($SiteCode)” } } } catch [System.UnauthorizedAccessException] { Write-Warning -Message “Access denied” ; break } catch [System.Exception] { Write-Warning -Message “Unable to determine Site Code” ; break } } Process { # Define update check variables $UpdateCheckCount = 0 $PrereqCheckCount = 0 $UpdatePackageAvailibility = $false # Check for an available UpdatePackage matching the specified version $CMUpdatePackage = Get-WmiObject -Namespace “root\SMS\site_$($SiteCode)” -Class SMS_CM_UpdatePackages -ComputerName $SiteServer -Filter “(Name like ‘Configuration Manager $($Version)%’) AND (UpdateType = 0) AND (State != 196612)” -Verbose:$false if (($CMUpdatePackage | Measure-Object).Count -eq 1) { do { # Increment UpdateCheckCount $UpdateCheckCount++ # Query SMS_CM_UpdatePackages WMI class for UpdatePackage instance Write-Verbose -Message “Configuration Manager Servicing: Attempting to locate Update Package in SMS_CM_UpdatePackages matching ‘Configuration Manager $($Version)'” $CMUpdatePackage = Get-WmiObject -Namespace “root\SMS\site_$($SiteCode)” -Class SMS_CM_UpdatePackages -ComputerName $SiteServer -Filter “(Name like ‘Configuration Manager $($Version)%’) AND (UpdateType = 0)” -Verbose:$false # Determine whether UpdatePackage is available for installation if ($CMUpdatePackage -eq $null) { Write-Verbose -Message “Configuration Manager Servicing ($($UpdateCheckCount) / $($AvailabilityCheckCount)): UpdatePackage was not found matching ‘Configuration Manager $($Version)’, sleeping for 30 seconds” } else { Write-Verbose -Message “Configuration Manager Servicing ($($UpdateCheckCount) / $($AvailabilityCheckCount)): UpdatePackage found, validating if $($CMUpdatePackage.Name.TrimEnd()) is ready for installation” switch ($CMUpdatePackage.State) { 327682 { Write-Verbose -Message “Configuration Manager Servicing ($($UpdateCheckCount) / $($AvailabilityCheckCount)): UpdatePackage state is Downloading, sleeping for 30 seconds” if ($UpdateCheckCount -eq $AvailabilityCheckCount) { Write-Verbose -Message “Configuration Manager Servicing ($($UpdateCheckCount) / $($AvailabilityCheckCount)): Downloading state detected for longer than $($AvailabilityCheckCount * 30 / 60) minutes, restarting SMS_EXECUTIVE service” if ($PSCmdlet.ShouldProcess(“SMS_EXECUTIVE”, “Restart”)) { Restart-Service -Name “SMS_EXECUTIVE” -Force -Verbose:$false } $UpdateCheckCount = 0 } } 262146 { Write-Verbose -Message “Configuration Manager Servicing ($($UpdateCheckCount) / $($AvailabilityCheckCount)): UpdatePackage state is Available, attempting to initiate installation of $($CMUpdatePackage.Name.TrimEnd())” $UpdatePackageAvailibility = $true } } } # Wait until UpdatePackage is available if ($UpdatePackageAvailibility -eq $false) { Start-Sleep -Seconds 30 } } while ($CMUpdatePackage.State -ne 262146) # Initiate UpdatePackage installation Write-Verbose -Message “Configuration Manager Servicing: Starting prerequisite checks for $($CMUpdatePackage.Name.TrimEnd())” $CMUpdatePackage = Get-WmiObject -Namespace “root\SMS\site_$($SiteCode)” -Class SMS_CM_UpdatePackages -ComputerName $SiteServer -Filter “(Name like ‘Configuration Manager $($Version)%’) AND (UpdateType = 0)” -Verbose:$false if ($CMUpdatePackage -ne $null) { if ($PSCmdlet.ShouldProcess($CMUpdatePackage.Name.TrimEnd(), “Install”)) { $CMUpdatePackage.UpdatePrereqAndStateFlags(0,2) | Out-Null } } # Wait for prerequisite checks to complete, state 196609 equals Installing do { # Increment PrereqCheckCount $PrereqCheckCount++ # Query for UpdatePackage instance to get State for prerequisite checks if ($PrereqCheckCount -eq $PrerequisiteCheckCount) { Write-Verbose -Message “Configuration Manager Servicing ($($PrereqCheckCount) / $($PrerequisiteCheckCount)): Prerequisite checks has been in running state for $($PrerequisiteCheckCount * 30 / 60) min, restarting SMS_EXECUTIVE service” if ($PSCmdlet.ShouldProcess(“SMS_EXECUTIVE”, “Restart”)) { Restart-Service -Name “SMS_EXECUTIVE” -Force -Verbose:$false } $PrereqCheckCount = 0 } else { Write-Verbose -Message “Configuration Manager Servicing ($($PrereqCheckCount) / $($PrerequisiteCheckCount)): Waiting for prerequisite checks to complete for $($CMUpdatePackage.Name.TrimEnd()), sleeping for 30 seconds” $CMUpdatePackage = Get-WmiObject -Namespace “root\SMS\site_$($SiteCode)” -Class SMS_CM_UpdatePackages -ComputerName $SiteServer -Filter “(Name like ‘Configuration Manager $($Version)%’) AND (UpdateType = 0)” -Verbose:$false # Wait until UpdatePackage prerequisite checks has completed Start-Sleep -Seconds 30 } } while ($CMUpdatePackage.State -ne 196609) # Output that Configuration Manager Servicing has begun Write-Verbose -Message “Configuration Manager Servicing: Installation was successfully initated for $($CMUpdatePackage.Name.TrimEnd()), for more details, review the CMUpdate.log” } elseif (($CMUpdatePackage | Measure-Object).Count -gt 1) { Write-Warning -Message “Query for Update Packages returned more than 1 instance, please define your search with a specific version” } else { Write-Warning -Message “Query for Update Packages did not return any instances” } }
Results
You’d run this script like any other script that I’ve created, by opening a PowerShell console, browsing to location where you’ve stored it and then simply call it with a set of parameters. See an example below:
.\Invoke-CMUpdatePackage.ps1 -SiteServer CM01 -Version 1606
By adding the Verbose parameter to the command above, you’ll get some detailed information on what’s currently going on. If not, the script will simply just hang there until it’s capable of initiating the update package for installation. When you run the script, you’ll see that it’s looking for an Update Package. If an eligible Update Package is found, it will check the current state and determine the proper action, like shown in the picture below:
If the script determines that it have to restart the SMS_EXECUTIVE service, hopefully the download process for the Update Package should begin, like shown in the picture below:
Finally, when the Update Package has been downloaded and the prerequisites checks have been completed, the script will then quit once the state has been changed to Installing, meaning that the Update Package for ConfigMgr is being serviced and installed, like shown below:
You can now follow the installation progress for the Update Package in CMUpdate.log.
Summary
As you may have understood from reading the documentation part for this script, it’s probably “faster” to just leverage the ConfigMgr console to update to the desired version of ConfigMgr. So why should you use this script? To be honest, I’d be very careful when using it in your production environment, however, in a lab environment it makes more sense to be able to have an automated build of ConfigMgr that brings you to the latest bits. This script will help you accomplishing that, but depending on your environment, it could take a little while for it to initiate the update and servicing. Please let me know if you have any issues with this script, as it’s a work in progress and also due to the fact that the Updates and Servicing feature in ConfigMgr is always improving.
Hi Nikolaj, great post and I agree with you we need an officiel cmdlet to handle the upgrade servicing.
Did you test your script if it works with the KB’s offered in the servicing node? e.g https://support.microsoft.com/da-dk/kb/3202796
Hi Rene,
I’ve only tested and verified for Update Packages, but essentially there should be no difference if they’re also made available in the same class.
Regards,
Nickolaj