[download id=”95″]

The Challenge

Microsoft has changed the way updates are being installed and uninstalled many times. This means that a common method can’t be used.

Windows XP and Windows 2003

  • Updates can be uninstalled from a hidden “uninstall” folder, located in the windows folder.  Windows 2008 R2 and Windows 7
  • A command line utility (wusa.exe) can uninstall the updates.

The solution

The solution contains one VBScript, this script is able to detect the local OS, and use one of the 2 methods above for uninstall.

XP/2003:

  • The script does a check for the uninstall folder, and run the spuninst.exe
  • The script writes event log event (Source: WSH) in the local event log describing if it was found it not.
  • More logging can be enabled by the /log argument, and a text field will be created on the local machine

Win7/2k8R2:

  • Script runs built-in the wusa.exe command.
  • The script does no check if the update is installed before trying to uninstall the update, since wusa.exe have no method for doing this.
  • Wusa.exe writes status messages to event log.
  • Wusa.exe supported /log argument, but it does not write text files, therefore a .evt file will be saved (if a path for another type of file is specified, it will automatically be changed to .evt to make the file usable.)

The Flowchart later in this document describes this in a more detailed way.

The script will end with success, no matter if the update exists on the machine or not. If it fails, something else is wrong (for example: bad command line in the ConfigMgr Program).

 

Known Issues

  • Windows 2008 (non R2) is Not Supported. It does not have the “old” uninstall folders in Windows folder, and “wusa.exe” utility does not support uninstallation.
  • Some updates cannot be uninstalled, even if we try to do it manually.
  • Other updates for Microsoft applications might not be possible to uninstall in this generic way
  • 3rd party application updates or service packs cannot be uninstalled.
  • Specific updates might use a non-standard installation method, which makes it impossible to uninstall (at least on XP/2003)

Using the script

Run the script with cscript.exe.

This is an example of the basic command line for running the script:

 

cscript.exe UninstallUpdates.vbs 976902

 

Syntax is

 

cscript.exe <scriptname> <kb number> <arguments>

 

this means you are able to use arguments for changing the behavior of the uninstall

 

 

Supported Arguments:
Argument Description
/passive This disables the /quiet argument, and makes a progressbar visible for the user.(requires the user to be able to interact with the program in ConfigMgr)
/norestart Disable automatic restart when using /quiet
/forcerestart Force a restart after uninstall, no matter if it is needed or not.
/warnrestart Warn user before restarting(requires the user to be able to interact with the program in ConfigMgr)
/log:”C:\temp\uninstall.log” Create a log file on the local machine.XP/2003: Text file

7/2K8R2: .evt eventlog file.

Notice there is no space between /log: and the path.

Allways surround the path with “ “ to make sure that it is parsed correctly.

To use argument in a command line

cscript.exe UninstallUpdates.vbs 976902 /norestart /log:”C:\KB976902-Uninstall.log”

Exit Codes

The script has a couple of custom exit codes.

You might experience other exit codes, if you have error in script, command line or similar.

Exit Code Description
10001 No arguments supplied, at least one argument (KB Number) is required!

 

a Thank you goes out to DFDS A/S

Script

 

' //***************************************************************************
' // ***** Script Header *****
' //
' // Solution:  ConfigMgr Software Updates
' // File:      UninstllUpdates-v0.1.vbs
' // Author:	Jakob Gottlieb Svendsen, Coretech A/S. https://blog.ctglobalservices.com
' // Purpose:   Uninstall Updates from Windows
' // 			Supported OS:
' //				Windows XP
' //				Windows 7
' //				Windows Server 2003
' //				Windows Server 2008 R2
' //
' // Usage:     wscript.exe <scriptname> <KB Number> <Arguments)
' //
' // 'Supported Arguments
' //	/passive (no quiet install)
' //	/norestart
' //	/forcerestart
' //	/warnrestart
' //	/promptrestart (remember to make the program in configmgr to be interactive)
' //	/log:"<path>"
' //
' //	fx: cscript.exe UninstallUpdates-v0.1.vbs 978654 /norestart /log:"C:\log.log"
' //
' // CORETECH A/S History:
' // 1.0.0     JGS 25/07/2011  Created initial version.
' // 1.0.1     JGS 10/02/2012  Disabled check for KB number, since they can be both 6 or 7 decimals (maybe even more)
' //
' // Customer History:
' //
' // ***** End Header *****
' //***************************************************************************
'//----------------------------------------------------------------------------
'//
'//  Global constant and variable declarations
'//
'//----------------------------------------------------------------------------

Set objShell = WScript.CreateObject("Wscript.Shell")

'//----------------------------------------------------------------------------
'//  Main routines
'//----------------------------------------------------------------------------

'Get Settings and KB
i = 0

strCommandArgs = " "

If WScript.Arguments.Count > 0 Then

	'WScript.Echo WScript.Arguments.Item(i)
	Do While i < WScript.Arguments.length

		If i = 0 Then
			strKB = Replace(WScript.Arguments.Item(i),"KB","")

			'Check regex.
'			Set objRE = New RegExp
'			objRE.Global     = True
'			objRE.IgnoreCase = False
'			objRE.Pattern    = "^\d{6}$"

'			bMatch = objRE.Test(strKB)
'			If Not bMatch Then
'				WScript.Echo "KB Number wrong format: " & strKB
'	  			WScript.Quit(10002) 'KB argument wrong format.
'			End If
		Else
			strArg = LCase(WScript.Arguments.Item(i))

			If InStr(strArg, "/log") Then
				bLog = True
				strLogFilePath = Replace(strArg, "/log:", "")
				'More log in the select case below
			Else
				If InStr(strArg, "/passive") Then
					bPassive = True
				Else
					strCommandArgs = strCommandArgs & " " & strArg
				End If
			End If

		End If

		i = i + 1

	Loop
Else
	WScript.Quit(10001) 'No arguments supplied
End If

strCurrentOSVersion = CheckLocalOSVersion

Select Case strCurrentOSVersion
	Case "6.1" 'Win7 - Win2k8r2
		If bLog Then
			'Convert file to .evt
			strLogFilePath = Left(strLogFilePath,Len(strLogFilePath) - 3)
			strLogFilePath = strLogFilePath & "evt"
			strCommandArgs = strCommandArgs & " /log:""" & strLogFilePath & """"
		End If

		UninstallUpdateWin2K8R2Win7 strKB, strCommandArgs, bPassive

	Case "5.2", "5.1" 'windows 2003 or Windows XP
		If bLog Then
			strCommandArgs = strCommandArgs & " /log:""" & strLogFilePath & """"
		End If

		UninstallUpdateXP2003 strKB, strCommandArgs, bPassive
End Select

'//----------------------------------------------------------------------------
'//  Procedures
'//----------------------------------------------------------------------------

Function UninstallUpdateWin2K8R2Win7(strKBNumber, Args, bPassive)
	Dim objShell
	Set objShell = WScript.CreateObject("Wscript.Shell")

	If bPassive Then
		'run silent uninstall
		strCmd = "wusa.exe /kb:" & strKBNumber & " /uninstall /passive" & Args
	Else
		'run silent uninstall
		strCmd = "wusa.exe /kb:" & strKBNumber & " /uninstall /quiet" & Args
	End If
	WScript.Echo "Commandline: " & strCmd
	ReturnCode = objShell.Run(strCmd , , True)

	WScript.Echo "ReturnCode: " & ReturnCode

End Function

Function UninstallUpdateXP2003(strKBNumber, Args, bPassive)
	Dim objFSO, objShell
	Set objFSO = CreateObject("Scripting.FileSystemObject")
	Set objShell = WScript.CreateObject("Wscript.Shell")

	Set objFolder = objFSO.GetFolder(objShell.ExpandEnvironmentStrings("%WINDIR%"))
	Set colSubfolders = objFolder.Subfolders

	bFound = False

	For Each objSubfolder in colSubfolders
		strFolderName = LCase(objSubFolder.Name)
		If InStr(strFolderName, "ntuninstallkb") Then
			strCurrentKB = Mid(strFolderName, InStr(strFolderName, "kb") + 2, 6)
			If strCurrentKB = strKBNumber Then
				bFound = True
				'Uninstall KB
				strUninstallExe = objSubFolder.Path & "\spuninst\spuninst.exe"
				If (objFSO.FileExists(strUninstallExe)) Then
					If bPassive Then
						'run silent uninstall
						strCmd = strUninstallExe & " /passive " & Args
					Else
						'run silent uninstall
						strCmd = strUninstallExe & " /quiet " & Args
					End If
					WScript.Echo "Commandline: " & strCmd
					ReturnCode = objShell.Run(strCmd , , True)
				Else
					objShell.LogEvent 1, "Software Update KB" & strKBNumber & " could not be uninstalled (uninstalled exe was not found at " & strUninstallExe & " )"
				End If
			End If
		End If
	    'Wscript.Echo objSubfolder.Name, objSubfolder.Size
	Next

	If bFound Then
		objShell.LogEvent 0, "Software Update KB" & strKBNumber & " was successfully uninstalled"
	Else
		objShell.LogEvent 1, "Software Update KB" & strKBNumber & " could not be uninstalled (folder not found)"
	End If

End Function

Function CheckLocalOSVersion
	Set objShell = CreateObject("Wscript.Shell")
	OSVer = objShell.RegRead("HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\CurrentVersion")
	CheckLocalOSVersion = OSVer
End Function

'//----------------------------------------------------------------------------
'//  End Script
'//----------------------------------------------------------------------------