SMS and SCCM give us the ability to build a process for full automated patch deployment. A healthy site with healthy clients generally leads to a “smooth” automated patch deployment process.

One of the first challenges I faced as an SMS/SCCM administrator, was answering the change management question “what is the rollback process for patch deployment?”  The only answer available is manual rollback (all hands on deck). This presents a major challenge if you do not have resources readily available during an emergency rollback scenario. Why not use your automated patch deployment tool to address this challenge.

In this article, I provide a method for rolling back security patches in line with Microsoft best practices. This process only applies to patches deployed to windows server 2003, Windows XP and below operating systems. I am working on updating the process for Vista and Windows Server 2008.

Background to process

This link provides the background to this rollback process Removing Windows software updates in the wrong order may cause the operating system to stop functioning.

The recommend method for rolling back patches is to remove patches in the reverse order of installation. This recommendation is based on the fact that most patches update the same DLLs etc. So in a scenario where 3 patches update the same DLL,

  • Install Patch 1 (DLL updated to V1 backup original DLL for rollback)
  • Install Patch 2 (DLL updated to V2 backup V1 DLL for rollback)
  • Install Patch 3 (DLL updated to V3 backup V2 DLL for rollback)

Removing patch 2 will return the DLL to V1 and lose the update made by patch 3. So how do we keep the system consistent and not lose other updates? . The answer is to rollback all patches and redeploy without the unwanted patch(es). Another challenge is, can this be automated?

In order to achieve the above, we first need to establish the original order of deployment and create an automated rollback deployment using SMS/SCCM software distribution.

Summary of process

  1. Query the client for all patches deployed and list by installation date time order.
  2. All patches for the latest date listed to be removed (in general deployments would be for same day and not across multiple days)
  3. Run spuninst.exe for the patch(es) to remove in the reverse order from the %SystemRoot%$NtUninstall[KBArticleNumber]$
  4. Steps 1 to 3 achieved with a VB script delivered as a standard software distribution package advertisement
  5. Initiate rollback by advertising to SMS/SCCM clients in scope using a collection

Script and Sample Screenshots

The script supplied is set to log only mode (need to change the test mode parameter to 1 for it to be in live mode). Both modes would create a hotfixundo.log file on the C: drive. Script kindly written by Gavin Woodall.

Copy the script to notepad and save as hotfix_undo_Live.vbs (or to any preferred name). In my case I have a package called Patch Rollback – Live. The Data Source is a package directory called Patch_Rollback (store the vbs script here and reference during package creation)

image image

Create a program for the package using the following command line : cscript %scriptname% (in my case %scriptname% = Hotfix_undo_live.vbs). Ensure that the program is set to run whether or not a user is logged on for non interactive deployments/advertisements.

image image

Create an advertisement for the package. Do not leave on a recurring schedule!!! – This would remove all patches from the targeted clients.


SMS 2003 Process: After each rollback create a new program (by default you will not be able to use the same program again if it has successfully run on a client). I create a new program every month just to be sure.

SCCM Process note: SCCM overcomes the SMS 2003 limitation because programs can be rerun even when successful


Copy Below to notepad and save as hotfix_undo_live.vbs (change testmode to 0 to make live)

‘ Script to enumerate last applied hotfixes, and rollback

on error resume next
const forappending = 8
const forwriting=2
const forreading=1
Const dictKey  = 1
Const dictItem = 2

‘ **********set to 0 to get out of testmode**********
‘ ***************************************************


Set fso = createObject(“Scripting.FileSystemObject”)
set windir=fso.GetSpecialFolder(0)
call stamplog(“*************************************************************”)
call stamplog(“Starting process, windows directory is “&windir.path)

‘ enumerate subfolders, check date.
For Each Subfolder in windir.SubFolders
if instr(lcase(,”$ntuninstall”)<>0 then
if lastdate=”” then
end if
if datediff(“d”,lastdate,subfolder.datecreated)>1 then
end if
end if

call stamplog(“Latest date found for uninstall folder is “&lastdate)
‘ loop again, creating a list of directories to be targeted.

set list = CreateObject(“Scripting.Dictionary”)

call stamplog (“Processing the following directories:”)
For Each Subfolder in windir.SubFolders
if instr(lcase(,”$ntuninstall”)<>0 then
if datediff(“d”,lastdate,subfolder.datecreated)<1 and datediff(“d”,lastdate,subfolder.datecreated)>=0 then
list.add subfolder.datecreated,subfolder.path
call stamplog(subfolder.path)
end if
end if

‘ sort dictionary
sortdictionary list,dictkey

‘ loop through list, shell out to run spuninst for each directory, last first
for each location in list
call stamplog(“Launching “& list.item(location)&”spuninstspuninst.exe”)
if testmode=0 then
run list.item(location)&”spuninstspuninst.exe /quiet /passive /norestart”
call stamplog(“***TESTMODE – Uninstall NOT run***”)
end if
call stamplog(“Finished at “&date&” “&time)

‘ Stamp line of text to specified logfile
sub stamplog(text)
Set objFSO = CreateObject(“Scripting.FileSystemObject”)
Set objTextFile = objFSO.OpenTextFile _
(logpath, Forappending, True)
wscript.echo (text)
end sub

‘ Run function
Function Run (ByVal cmd)
Dim sh: Set sh = CreateObject(“WScript.Shell”)
Dim wsx: Set wsx = Sh.Exec(cmd)
If wsx.ProcessID = 0 And wsx.Status = 1 Then
‘ (The Win98 version of VBScript does not detect WshShell.Exec errors)
Err.Raise vbObjectError,,”WshShell.Exec failed.”
End If
Dim Status: Status = wsx.Status
WScript.StdOut.Write wsx.StdOut.ReadAll()
WScript.StdErr.Write wsx.StdErr.ReadAll()
If Status <> 0 Then Exit Do
WScript.Sleep 10
Run = wsx.ExitCode
End Function

‘ Runs an internal command interpreter command.
Function RunCmd (ByVal cmd)
RunCmd = Run(“%ComSpec% /c ” & cmd)
End Function

‘ Sort function
Function SortDictionary(objDict,intSort)
‘ declare our variables
Dim strDict()
Dim objKey
Dim strKey,strItem
Dim X,Y,Z

‘ get the dictionary count
Z = objDict.Count

‘ we need more than one item to warrant sorting
If Z > 1 Then
‘ create an array to store dictionary information
ReDim strDict(Z,2)
X = 0
‘ populate the string array
For Each objKey In objDict
strDict(X,dictKey)  = CStr(objKey)
strDict(X,dictItem) = CStr(objDict(objKey))
X = X + 1

‘ perform a a shell sort of the string array
For X = 0 to (Z – 2)
For Y = X to (Z – 1)
If StrComp(strDict(X,intSort),strDict(Y,intSort),vbTextCompare) > 0 Then
strKey  = strDict(X,dictKey)
strItem = strDict(X,dictItem)
strDict(X,dictKey)  = strDict(Y,dictKey)
strDict(X,dictItem) = strDict(Y,dictItem)
strDict(Y,dictKey)  = strKey
strDict(Y,dictItem) = strItem
End If

‘ erase the contents of the dictionary object

‘ repopulate the dictionary with the sorted information
For x=(z-1) to 0 step -1
‘    For X = 0 to (Z – 1)
objDict.Add strDict(X,dictKey), strDict(X,dictItem)

End If

End Function

Additional Notes:

Every security update has a Removal information section listed under Security Update Deployment. So for MS09-001 you would find below for the XP operating system

Removal Information

Use Add or Remove Programs tool in Control Panel or the Spuninst.exe utility located in the %Windir%$NTUninstallKB958687$Spuninst folder

Tagged on: