SCCM - Fix Patch Issues
This is just a script I have put together to try to fix common patch issues. It is based off of the Microsoft articles referenced in the code. Their fix was in EXE form and I wanted the flexibility to enabled/disable certain things.
Generally, this is meant to be advertised to all clients without specifying a Mandatory Execution time so that it can easily be run ad-hoc.
'check for running jobs 'check for active patch deployments 'check windows update agent version? 'http://support.microsoft.com/kb/971058 'http://support.microsoft.com/kb/822798 'use "wuauclt.exe /resetauthorization /detectnow" ??? 'Do we want to do this? ' Method 9: Clear the temporary file and restart the hotfix installation or the service pack installation ' Note Skip this method if the operating system is Windows 2000. ' ' To clear the temporary file and restart the hotfix installation or the service pack installation, follow these steps: ' Delete all the tmp*.cat files in the following folders: ' ' %systemroot%\system32\CatRoot\{127D0A1D-4EF2-11D1-8608-00C04FC295EE} ' ' %systemroot%\system32\CatRoot\{F750E6C3-38EE-11D1-85E5-00C04FC295EE} ' Delete all the kb*.cat files in the following folders: ' %systemroot%\System32\CatRoot\{F750E6C3-38EE-11D1-85E5-00C04FC295EE} ' %systemroot%\System32\CatRoot\{127D0A1D-4EF2-11D1-8608-00C04FC295EE} Option Explicit Dim oFSO, WshShell Dim sSystemRootLocation Dim blnStopBITS, blnStopWUAUSERV, blnStartBITS, blnStartWUAUSERV Dim sOSFromReg Const sWBEMServiceName = "WinMgmt" Const sSCCMServiceName = "CCMExec" Const ForReading = 1, ForWriting = 2, ForAppending = 8 Set oFSO = CreateObject("Scripting.FileSystemObject") Set WshShell = CreateObject("WScript.Shell") sSystemRootLocation = WshShell.RegRead("HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\SystemRoot") sOSFromReg = GetOSFromReg WScript.Echo sOSFromReg 'Stop BITS/Windows Update Services Call WriteLogEntry("Stopping Services", True) blnStopBITS = StopServiceSC("BITS") blnStopWUAUSERV = StopServiceSC("WUAUSERV") Call RebuildWMI 'Delete QMGR files Call DeleteQMGRFiles 'Delete old Patch files Call Deltree(sSystemRootLocation & "\SoftwareDistribution\DataStore") Call Deltree(sSystemRootLocation & "\SoftwareDistribution\Download") Call Deltree(sSystemRootLocation & "\system32\catroot2") 'Reset Security Descriptors WshShell.Run "sc.exe sdset bits D:(A;;CCLCSWRPWPDTLOCRRC;;;SY)(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;BA)(A;;CCLCSWLOCRRC;;;AU)(A;;CCLCSWRPWPDTLOCRRC;;;PU)", 0, True WshShell.Run "sc.exe sdset wuauserv D:(A;;CCLCSWRPWPDTLOCRRC;;;SY)(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;BA)(A;;CCLCSWLOCRRC;;;AU)(A;;CCLCSWRPWPDTLOCRRC;;;PU)", 0 , True 'Register DLLs Call WriteLogEntry("Registering DLLs", True) Call RegisterWUDLLs 'Reset WinSock (KB article has the command wrong) Call WriteLogEntry("Resetting Winsock (" & sSystemRootLocation & "\system32\NETSH.EXE WINSOCK RESET)", True) WshShell.Run sSystemRootLocation & "\system32\NETSH.EXE WINSOCK RESET", 0, True 'winxp/server 2003 only If sOSFromReg = "Windows XP" Or sOSFromReg = "Windows Server 2003" Then Call WriteLogEntry("XP/2003 (proxycfg.exe -d)", True) WshShell.Run "proxycfg.exe -d", 0, True End If 'Start BITS/Windows Update Services Call WriteLogEntry("Starting Services", True) blnStartBITS = StartServiceSC("BITS") blnStartWUAUSERV = StartServiceSC("WUAUSERV") 'vista/win7/2008 only If sOSFromReg = "Windows Vista" Or sOSFromReg = "Windows 7" Then Call WriteLogEntry("Vista/Win7/2008 (bitsadmin.exe /reset /allusers)", True) WshShell.Run "bitsadmin.exe /reset /allusers", 0, True End If Call RefreshServerComplianceState WScript.Quit '*************************************************************************************** Sub RefreshServerComplianceState() Call WriteLogEntry("Refreshing Server Compliance State", True) ' Initialize the UpdatesStore variable. Dim newCCMUpdatesStore ' Create the COM object. Set newCCMUpdatesStore = CreateObject ("Microsoft.CCM.UpdatesStore") ' Refresh the server compliance state by running the RefreshServerComplianceState method. newCCMUpdatesStore.RefreshServerComplianceState ' Output success message. 'wscript.echo "Ran RefreshServerComplianceState." End Sub Function RegisterWUDLLs Call RegisterDLL(sSystemRootLocation & "\system32\atl.dll") Call RegisterDLL(sSystemRootLocation & "\system32\urlmon.dll") Call RegisterDLL(sSystemRootLocation & "\system32\mshtml.dll") Call RegisterDLL(sSystemRootLocation & "\system32\shdocvw.dll") Call RegisterDLL(sSystemRootLocation & "\system32\browseui.dll") Call RegisterDLL(sSystemRootLocation & "\system32\jscript.dll") Call RegisterDLL(sSystemRootLocation & "\system32\vbscript.dll") Call RegisterDLL(sSystemRootLocation & "\system32\scrrun.dll") Call RegisterDLL(sSystemRootLocation & "\system32\msxml.dll") Call RegisterDLL(sSystemRootLocation & "\system32\msxml3.dll") Call RegisterDLL(sSystemRootLocation & "\system32\msxml6.dll") Call RegisterDLL(sSystemRootLocation & "\system32\actxprxy.dll") Call RegisterDLL(sSystemRootLocation & "\system32\softpub.dll") Call RegisterDLL(sSystemRootLocation & "\system32\wintrust.dll") Call RegisterDLL(sSystemRootLocation & "\system32\dssenh.dll") Call RegisterDLL(sSystemRootLocation & "\system32\rsaenh.dll") Call RegisterDLL(sSystemRootLocation & "\system32\gpkcsp.dll") Call RegisterDLL(sSystemRootLocation & "\system32\sccbase.dll") Call RegisterDLL(sSystemRootLocation & "\system32\slbcsp.dll") Call RegisterDLL(sSystemRootLocation & "\system32\cryptdlg.dll") Call RegisterDLL(sSystemRootLocation & "\system32\oleaut32.dll") Call RegisterDLL(sSystemRootLocation & "\system32\ole32.dll") Call RegisterDLL(sSystemRootLocation & "\system32\shell32.dll") Call RegisterDLL(sSystemRootLocation & "\system32\initpki.dll") Call RegisterDLL(sSystemRootLocation & "\system32\wuapi.dll") Call RegisterDLL(sSystemRootLocation & "\system32\wuaueng.dll") Call RegisterDLL(sSystemRootLocation & "\system32\wuaueng1.dll") Call RegisterDLL(sSystemRootLocation & "\system32\wucltui.dll") Call RegisterDLL(sSystemRootLocation & "\system32\wups.dll") Call RegisterDLL(sSystemRootLocation & "\system32\wups2.dll") Call RegisterDLL(sSystemRootLocation & "\system32\wuweb.dll") Call RegisterDLL(sSystemRootLocation & "\system32\qmgr.dll") Call RegisterDLL(sSystemRootLocation & "\system32\qmgrprxy.dll") Call RegisterDLL(sSystemRootLocation & "\system32\wucltux.dll") Call RegisterDLL(sSystemRootLocation & "\system32\muweb.dll") Call RegisterDLL(sSystemRootLocation & "\system32\wuwebv.dll") Call RegisterDLL(sSystemRootLocation & "\system32\mssip32.dll") 'kb822798 End Function Function RegisterDLL(sFilePath) Call WriteLogEntry(vbTab & "REGSVR32 /S " & sFilePath, True) '#### WshShell.Run "REGSVR32 /S " & sFilePath, 3, True End Function Function Deltree(sFolderPath) Dim oFolder, oFile, oSubFolder Call WriteLogEntry("DELTREE:" & vbTab & sFolderPath, True) '##### 'Exit Function If oFSO.FolderExists(sFolderPath) Then Set oFolder = oFSO.GetFolder(sFolderPath) For Each oFile In oFolder.Files Dim sFilePath sFilePath = oFile.Path 'Call WriteLogEntry(vbTab & vbTab & "FILEDELETE:" & vbTab & sFilePath, True) '********************************** On Error Resume Next oFSO.DeleteFile sFilePath, True On Error GoTo 0 '********************************** Next For Each oSubFolder In oFolder.SubFolders Dim sSubFolderPath sSubFolderPath = oSubFolder.Path Call Deltree(sSubFolderPath) 'Call WriteLogEntry(vbTab & vbTab & "FOLDERDELETE2:" & vbTab & sSubFolderPath, True) '********************************** On Error Resume Next If oFSO.FolderExists(sSubFolderPath) Then oFSO.DeleteFolder(sSubFolderPath) On Error GoTo 0 '********************************** Next 'Call WriteLogEntry(vbTab, vbTab & "FOLDERDELETE1:" & vbTab & sFolderPath) On Error Resume Next If oFSO.FolderExists(sFolderPath) Then oFSO.DeleteFolder(sFolderPath) On Error GoTo 0 Else Call WriteLogEntry(vbTab & "DELTREE Warning - Path not found:" & vbTab & sFolderPath, True) End If If oFSO.FolderExists(sFolderPath) Then Deltree = False 'Failed if the folder is still there Else Deltree = True 'Succeeded if the folder is gone End If End Function 'Deltree Function DeleteQMGRFiles Dim sAllUsersProfile, oAllUsersProfile, oFile sAllUsersProfile = WshShell.ExpandEnvironmentStrings("%ALLUSERSPROFILE%") Set oAllUsersProfile = oFSO.GetFolder(sAllUsersProfile & "\Application Data\Microsoft\Network\Downloader") For Each oFile In oAllUsersProfile.Files 'WScript.Echo oFile 'WScript.Echo vbTab & oFile.Name If UCase(Left(oFile.Name, 4)) = "QMGR" And UCase(Right(oFile.Name, 4)) = ".DAT" Then Call WriteLogEntry("DELETE: " & oFile, True) 'WScript.Echo vbTab & "DELETE: " & oFile '##### oFile.Delete End If Next End Function Function GetOSType 'http://technet.microsoft.com/en-us/library/cc782360(WS.10).aspx Dim sOStype sOStype = WshShell.RegRead("HKLM\SYSTEM\CurrentControlSet\Control\ProductOptions\ProductType") Select Case UCase(sOStype) Case "WINNT" : GetOSType = "WORKSTATION" Case "SERVERNT", "LANMANNT" : GetOSType = "SERVER" Case Else : GetOSType = "UNKNOWN" End Select End Function 'GetOSType Function GetOSFromReg 'Determines the OS based on Registry entries (only used when WMI is broken) Dim sOSVersion, sOSName, sOSType sOSType = GetOSType 'sProductType = WshShell.RegRead("HKLM\SYSTEM\CurrentControlSet\Control\ProductOptions\ProductType") sOSVersion = WshShell.RegRead("HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\CurrentVersion") Select Case True Case (sOSVersion = "6.1" And sOSType = "WORKSTATION") sOSName = "Windows 7" Case sOSVersion = "6.0" And sOSType = "WORKSTATION" sOSName = "Windows Vista" Case sOSVersion = "5.1" And sOSType = "WORKSTATION", sOSVersion = "5.2.3790" And sOSType = "WORKSTATION" sOSName = "Windows XP" Case sOSVersion = "5.0" And sOSType = "WORKSTATION" sOSName = "Windows 2000" Case sOSVersion = "6.1" And sOSType = "SERVER" sOSName = "Windows Server 2008 R2" Case sOSVersion = "6.0" And sOSType = "SERVER" sOSName = "Windows Server 2008" Case sOSVersion = "5.2" And sOSType = "SERVER" sOSName = "Windows Server 2003" Case Else sOSName = "UNKNOWN" End Select Call WriteLogEntry("GetOSFromReg (" & sOSVersion & "/" & sOSType & " = " & sOSName & ")", True) GetOSFromReg = sOSName End Function 'GetOSFromReg Sub WriteLogEntry(sLogText, blnAlwaysLog) 'Logging sub.. 'If blnAlwaysLog for a specific line is False, only logs entry if blnDEBUG Constant is set to True WScript.Echo sLogText ' If blnDEBUG = True Then ' oLogfile.WriteLine(Now & vbTab & sComputer & vbTab & sLogText) ' Else ' If blnAlwaysLog = True Then ' oLogfile.WriteLine(Now & vbTab & sComputer & vbTab & sLogText) ' End If ' End If End Sub 'WriteLogEntry Function CheckServiceStateSC(sServiceName) Dim sCmdLine, oExec, intReturn, sOutput Dim sStateLine, arrStateLine1, arrStateLine2, intServiceState, sServiceState sCmdLine = "sc QUERY " & sServiceName 'WScript.Echo sCmdLine Set oExec = WshShell.Exec(sCmdLine) Do While Not oExec.StdOut.AtEndOfStream Dim sOutputLine sOutputLine = oExec.StdOut.ReadLine If InStr(1, sOutputLine, "STATE", vbTextCompare) 0 Then sStateLine = sOutputLine Exit Do 'Checking one service-exit once we find what we need End If sOutput = sOutput & sOutputLine Loop 'Make sure we are done... Do While oExec.Status 1 WScript.Sleep 100 Loop Err.Clear On Error Resume Next arrStateLine1 = Split(sStateLine, ":") 'untested method to check if we don't get a response If Err.Number 0 Then CheckServiceStateSC = "9999" On Error GoTo 0 Exit Function Else arrStateLine2 = Split(arrStateLine1(1), " ") intServiceState = arrStateLine2(1) sServiceState = arrStateLine2(3) End If On Error GoTo 0 '1 STOPPED '2 START_PENDING '3 STOP_PENDING '4 RUNNING '1056 = already running Call WriteLogEntry(vbTab & vbTab & "CheckServiceStateSC:" & vbTab & sServiceName & vbTab & intServiceState & vbTab & sServiceState, False) CheckServiceStateSC = intServiceState End Function 'CheckServiceStateSC Function StopServiceSC(sServiceName) Dim sCmdLine, oExec, intReturn, intServiceState Dim intCounter sCmdLine = "sc STOP " & sServiceName 'WScript.Echo sCmdLine Call WriteLogEntry(vbTab & "Stopping Service:" & vbTab & sServiceName, True) Set oExec = WshShell.Exec(sCmdLine) Do While oExec.Status 1 WScript.Sleep 100 Loop intReturn = oExec.ExitCode 'WScript.Echo vbTab & vbTab & "StopServiceSC:" & vbTab & intReturn If intReturn = 0 Then intServiceState = CheckServiceStateSC(sServiceName) Do While intServiceState 1 intCounter = intCounter + 1 If intCounter > 10 Then 'Give up after 10 attempts StopServiceSC = False Exit Function End If WScript.Sleep 2000 intServiceState = CheckServiceStateSC(sServiceName) Loop StopServiceSC = True Else 'Error reported by SC when issuing stop command '1051 = Dependent services still running StopServiceSC = False End If End Function 'StopServiceSC Function StartServiceSC(sServiceName) Dim sCmdLine, oExec, intReturn, intServiceState Dim intCounter sCmdLine = "sc START " & sServiceName 'WScript.Echo vbTab & sCmdLine Call WriteLogEntry(vbTab & "Starting Service:" & vbTab & sServiceName, True) Set oExec = WshShell.Exec(sCmdLine) Do While oExec.Status 1 WScript.Sleep 100 Loop intReturn = oExec.ExitCode '1056 = already running 'WScript.Echo vbTab & vbTab & "StartServiceSC:" & vbTab & intReturn StartServiceSC = intReturn intServiceState = CheckServiceStateSC(sServiceName) Do While intServiceState 4 intCounter = intCounter + 1 If intCounter > 10 Then 'Give up after 10 attempts StartServiceSC = False Exit Function End If WScript.Sleep 2000 intServiceState = CheckServiceStateSC(sServiceName) Loop StartServiceSC = True End Function 'StartServiceSC Function RebuildWMI 'Stops WMI and all dependent services 'Renames Repository folder 'Starts WMI and all dependent services that were running 'Always starts CCMEXEC at the end Dim sWBEMRepositoryLocation, sWBEMFolderPath, sRepositoryFolderRenamedPath Dim blnDeltree Call WriteLogEntry("Attempting to rebuild WMI", True) 'Some OSs store the Repository location using %systemroot%... 'sSystemRootLocation = WshShell.RegRead("HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\SystemRoot") 'Call WriteLogEntry(vbTab & "SystemRootLocation:" & vbTab & sSystemRootLocation, False) 'Need to look in the reg to find the repository location because some tools like SMS Client Center move the Repository location sWBEMRepositoryLocation = WshShell.RegRead("HKLM\SOFTWARE\Microsoft\WBEM\CIMOM\Repository Directory") sWBEMRepositoryLocation = Replace(sWBEMRepositoryLocation, "%systemroot%", sSystemRootLocation, 1, -1, vbTextCompare) Call WriteLogEntry(vbTab & "WBEMRepositoryLocation:" & vbTab & sWBEMRepositoryLocation, False) If sWBEMRepositoryLocation "FUNCTIONERROR" Then 'Make sure Repository folder exists If oFSO.FolderExists(sWBEMRepositoryLocation) Then Call WriteLogEntry(vbTab & "Folder Exists:" & vbTab & sWBEMRepositoryLocation, True) 'Get parent folder name sWBEMFolderPath = oFSO.GetParentFolderName(sWBEMRepositoryLocation) Call WriteLogEntry(vbTab & "WBEMFolderPath:" & vbTab & sWBEMFolderPath, True) 'Set name for repository to be renamed to sRepositoryFolderRenamedPath = sWBEMRepositoryLocation & "-old" Call WriteLogEntry(vbTab& "RepositoryFolderRenamedPath:" & vbTab & sRepositoryFolderRenamedPath, True) 'Delete any existing Repository backup folders If oFSO.FolderExists(sRepositoryFolderRenamedPath) = True Then Call WriteLogEntry(vbTab & "Backup Repository folder already exists - Deleting:" & vbTab & sRepositoryFolderRenamedPath, True) blnDeltree = Deltree(sRepositoryFolderRenamedPath) Call WriteLogEntry(vbTab & "Deltree Result:" & vbTab & blnDeltree, True) Else blnDeltree = True End If 'If removal of any Repository backup folders was successful... If blnDeltree = True Then Dim intWMIServiceState, intSCCMServiceState, oKey 'StopServiceAndDependents for WMI Service intWMIServiceState = CheckServiceStateSC(sWBEMServiceName) If intWMIServiceState = 1 Then 'Already stopped Else 'Get Dependent Services Dim oDictDependentSvcs Set oDictDependentSvcs = EnumServiceDepSC(sWBEMServiceName) If CheckServiceStateSC("ccmexec") 1 Then blnStopService = StopServiceSC("ccmexec") Call WriteLogEntry(vbTab & vbTab & "blnStopService - " & oKey & vbTab & blnStopService, True) End If 'Stop Dependent Services For Each oKey In oDictDependentSvcs.Keys Dim blnStopService blnStopService = StopServiceSC(oKey) Call WriteLogEntry(vbTab & vbTab & "blnStopService - " & oKey & vbTab & blnStopService, True) Next 'Stop WinMgmt Service Call StopServiceSC(sWBEMServiceName) End If 'NEED TO CONFIRM SERVICE IS STOPPED BEFORE MOVING ON intWMIServiceState = CheckServiceStateSC(sWBEMServiceName) If intWMIServiceState = 1 Then 'STOPPED Call WriteLogEntry(vbTab & "Renaming Folder '" & sWBEMRepositoryLocation & "' to '" & sRepositoryFolderRenamedPath & "'", True) 'Rename the active Repository folder oFSO.MoveFolder sWBEMRepositoryLocation, sRepositoryFolderRenamedPath 'Startup the WMI service Call StartServiceSC(sWBEMServiceName) intWMIServiceState = CheckServiceStateSC(sWBEMServiceName) 'Startup dependent services If intWMIServiceState = 4 Then 'RUNNING For Each oKey In oDictDependentSvcs.Keys Dim blnStartService blnStartService = StartServiceSC(oKey) Call WriteLogEntry(vbTab & vbTab & "blnStartService - " & oKey & vbTab & blnStartService, True) Next 'Startup CCMExec intSCCMServiceState = CheckServiceStateSC(sSCCMServiceName) If intSCCMServiceState = 1 Then 'STOPPED 'SCCM may not have been running before-If not, go ahead and start it now blnStartService = StartServiceSC(sSCCMServiceName) Call WriteLogEntry(vbTab & vbTab & "blnStartService - " & sSCCMServiceName & vbTab & blnStartService, True) End If Call WriteLogEntry("WMI REBUILD COMPLETE", True) Else Call WriteLogEntry(vbTab & "ERROR - WMI Service didn't start back up", True) End If Else Call WriteLogEntry(vbTab & "ERROR - WMI Service never stopped", True) End If Else Call WriteLogEntry(vbTab & "ERROR - Old Repository folder could not be removed", True) End If Else Call WriteLogEntry(vbTab & "ERROR - Cannot find folder:" & vbTab & sWBEMRepositoryLocation, True) End If Else Call WriteLogEntry(vbTab & "ERROR - Could not get WBEM Repository location from the registry", True) End If End Function 'RebuildWMI Function EnumServiceDepSC(sServiceName) Dim sCmdLine, oExec, oDictTemp Set oDictTemp = CreateObject("Scripting.Dictionary") : oDictTemp.CompareMode = vbTextCompare sCmdLine = "sc ENUMDEPEND " & sServiceName 'WScript.Echo sCmdLine Set oExec = WshShell.Exec(sCmdLine) Do While Not oExec.StdOut.AtEndOfStream Dim sOutputLine, sDepService, intServiceState sOutputLine = oExec.StdOut.ReadLine If InStr(1, sOutputLine, "SERVICE_NAME", vbTextCompare) 0 Then sDepService = Replace(sOutputLine, "SERVICE_NAME: ", "", 1, -1, vbTextCompare) 'WScript.Echo vbTab & sDepService End If If InStr(1, sOutputLine, "STATE", vbTextCompare) 0 Then intServiceState = ParseServiceStateLineSC(sOutputLine) 'WScript.Echo vbTab & intServiceState End If If sDepService "" And intServiceState "" Then 'We have info about one service If intServiceState = 4 Then oDictTemp.Add sDepService, Null End If sDepService = "" : intServiceState = "" End If Loop Set EnumServiceDepSC = oDictTemp End Function 'EnumServiceDepSC Function ParseServiceStateLineSC(sStateLine) Dim arrStateLine1, arrStateLine2, intServiceState, sServiceState arrStateLine1 = Split(sStateLine, ":") arrStateLine2 = Split(arrStateLine1(1), " ") intServiceState = arrStateLine2(1) sServiceState = arrStateLine2(3) ParseServiceStateLineSC = intServiceState End Function 'ParseServiceStateLineSC









