Samuel Erskine at myITforum.com

Geek by day, freak by night

SMS and SCCM Patch management – An automated security update rollback process

Introduction

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.

image

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

image

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**********
testmode=1
' ***************************************************

Logpath="C:\hotfixundo.log"

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

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

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(subfolder.name),"$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
    Next

' 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)&"\spuninst\spuninst.exe")
err.clear
if testmode=0 then
run list.item(location)&"\spuninst\spuninst.exe /quiet /passive /norestart"
else
call stamplog("***TESTMODE - Uninstall NOT run***")
end if
next
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)
objtextfile.writeline(text)
wscript.echo (text)
objTextFile.Close
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
   Do
      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
      Loop
   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
    Next

    ' 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
      Next
    Next

    ' erase the contents of the dictionary object
    objDict.RemoveAll

    ' 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)
    Next

  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 http://www.microsoft.com/technet/security/Bulletin/MS09-001.mspx

Removal Information

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

Comments

George Ryles said:

Patch rollback ability in <patch authority ultimate works well for me and you don't need any extra scripting.

www.scriptlogic.com/.../patchauthorityultimate

# March 13, 2009 12:01 PM