Update Deployment with SCCM

The following Process is an example of how to automatically patch Computers with System Center Configuration Manager.

Additional goals we want to acieve in the process:

  • review and approval of Software Updates in a custom Schedule (e.g. once per Month).
  • Once approved, the deployment must be fully automated.
  • The End-to-End process duration can be more than 1 Month.
  • No direct dependency on Microsoft-Schedules for the Process (e.g. Patch-Tuesday).
  • Ability to use the SCCM Console for monitoring and manual intervention.

Overview:

ADR

We are using one Automatic-Deployment-Rule that downloads all "Required >1" Updates to a Deployment-Package and distributes the Updates to an Update-Group called "Review". This Update-Group is not deployed (deployment is disabled) as it's just a Container for Reporting.

Updating large Deployment packages can take a while and clients may have issues downloading updates during this time. That's why the ADR must not be scheduled to run very frequent, just make sure it did run before you review the Updates.

The ADR contains all the Products and Classifications you want to include and in case of Problems, you can also exclude specific Updates in the ADR (e.g. KB3072779 is excluded in the following example).

Note: The ADR has a "Preview" Button... this gives you an Idea of how many Updates will be downloaded and distributed in the next ADR-Schedule.

Review

The Change-Advisory-Board must Review and approve/reject updates. So the Team-Members (the Engineer is not the only Member of that Team ! ) must be able to:

  • identify and evaluate security, technical and business impacts
  • aware of other running or planned changes
  • make decisions

To get the List of updates to Review, just click on "Show Members" of the Review Update Group:

If you want to automatically generate a Change-Request (e.g. in SCSM), you can use the following Script to extract the required Data from the Update Group "Review":

#Import SCCM PowerShell Module
import-module (Join-Path $(Split-Path $env:SMS_ADMIN_UI_PATH) ConfigurationManager.psd1)  
cd ((Get-PSDrive -PSProvider CMSite).Name + ':')

#Get the Update-Details...
(Get-CMSoftwareUpdateGroup -name "Review").Updates | % { Get-CMSoftwareUpdate -Id $_ -Fast} | Select -Property ArticleID, BulletinID, NumMissing, NumTotal, LocalizedDisplayName, CI_ID

Approve

If there are some Updates that must be excluded at all, the ADR must be modified to exclude these Article-Id's (see ADR Screenshot). For updates that are temporary "rejected" (they will come back the next time the ADR is triggered), just remove these Updates from the "Review" Group...

At the end, the "Review" Group must contain all approved Updates that are going to the next deployment-level.

Move to Test

Now we have to "move" the approved Updates from the "Review" Update-Group to the "Test" group. You can do this manually in the console, or by triggering the following PowerShell (e.g. as a Scheduled-Task with a manual Trigger):

#Import SCCM PowerShell Module
import-module (Join-Path $(Split-Path $env:SMS_ADMIN_UI_PATH) ConfigurationManager.psd1)  
cd ((Get-PSDrive -PSProvider CMSite).Name + ':')

#Move all Updates from Review to Test and remove all existing Updates in Test
$a = (Get-CMSoftwareUpdateGroup -name "Test")
$a.Item("Updates").IntegerArrayValue = (Get-CMSoftwareUpdateGroup -name "Review").Updates
$a.Put()

All existing Updates in Test will be removed; Test becomes an exact copy of the Review-Group.

Move to production

You can now use the same logic to move the Updates to the next Update-Group level. This can be done by using a Scheduled-Task that triggers the following PowerShell:

#Import SCCM PowerShell Module
import-module (Join-Path $(Split-Path $env:SMS_ADMIN_UI_PATH) ConfigurationManager.psd1)  
cd ((Get-PSDrive -PSProvider CMSite).Name + ':')

#Add Updates from Wave2 to the Rollup group
(Get-CMSoftwareUpdateGroup -name "Wave2").Updates | % { Add-CMSoftwareUpdateToGroup -SoftwareUpdateGroupname "Rollup" -SoftwareUpdateId $_ }

#Move all Updates from Wave1 to Wave2 and remove all existing Updates in Wave2
$a = (Get-CMSoftwareUpdateGroup -name "Wave2")
$a.Item("Updates").IntegerArrayValue = (Get-CMSoftwareUpdateGroup -name "Wave1").Updates
$a.Put()

#Move all Updates from Pilot to Wave1 and remove all existing Updates in Wave1
$a = (Get-CMSoftwareUpdateGroup -name "Wave1")
$a.Item("Updates").IntegerArrayValue = (Get-CMSoftwareUpdateGroup -name "Pilot").Updates
$a.Put()

#Move all Updates from Test to Pilot and remove all existing Updates in Pilot
$a = (Get-CMSoftwareUpdateGroup -name "Pilot")
$a.Item("Updates").IntegerArrayValue = (Get-CMSoftwareUpdateGroup -name "Test").Updates
$a.Put()

The Script starts with the last Wave (Rollup), to keep the History of the previous Groups.

We want to stagger the deployment over three different Waves. Each Wave should include Members from previous Waves (except Test and Pilot, they can contain Direct-Membership-Rules):
To randomize the Collections members, Wave1 is using the following Query:
select * from SMS_R_System where SMS_R_System.SMSUniqueIdentifier like "%[0-5]"

Wave2 has to include Wave1, so it's using the Query:
select * from SMS_R_System where SMS_R_System.SMSUniqueIdentifier like "%[0-A]"

The Rollup Collection does just include all devices (e.g. in the current screenshot it includes the "All managed Servers" collection)

Rollup

As we want to keep all approved Updates in the Rollup-Group, we need a different PowerShell to just add new Updates and keep the existing ones:

#Import SCCM PowerShell Module
import-module (Join-Path $(Split-Path $env:SMS_ADMIN_UI_PATH) ConfigurationManager.psd1)  
cd ((Get-PSDrive -PSProvider CMSite).Name + ':')

#Add Updates from Wave2 to the Rollup group
(Get-CMSoftwareUpdateGroup -name "Wave2").Updates | % { Add-CMSoftwareUpdateToGroup -SoftwareUpdateGroupname "Rollup" -SoftwareUpdateId $_ }

Deployments

If you are using Clients-Side Scripts (see: SCCM Patch-Management Tasks (client side) ) to trigger the Patch-Installation, you can set the deadlines on the deployments to a future date und suppress all reboots.

On Workstations, just deploy the Updates as required (with the option to: "show notifications for computer restarts") but suppress the reboot. Reporting or a Compliance Baseline may help to detect Computers that have not rebooted over x Days...

Cleanup

Over the Time, the Rollup Group will grow with unused and expired updates. You can use the following PowerShell Script to cleanup these Updates from the Group:

#Import SCCM PowerShell Module
import-module (Join-Path $(Split-Path $env:SMS_ADMIN_UI_PATH) ConfigurationManager.psd1)  
cd ((Get-PSDrive -PSProvider CMSite).Name + ':')

#Cleanup not required and expired Updates
$a = (Get-CMSoftwareUpdateGroup -name "Rollup")
$RequiredIDs = $a.Updates | % { $upd = get-CMSoftwareUpdate -Id $_ -Fast;if(($upd.NumMissing -ne 0) -and -not $upd.isExpired) { $_} }
$a = (Get-CMSoftwareUpdateGroup -name "Rollup")
$a.Item("updates").IntegerArrayValue = [int[]]$RequiredIDs
$a.Put()