Since patching is currently on the brain I thought that I'd also include this little VBScript that I wrote a while back to do a quick 'n dirty patch validation. The script takes two arguments 1) a file with the servers to target (one per line) and 2) a file with the hotfix ID that is expected to be installed (accepts form KB12345 or 12345, one per line). After the script is complete you will have a semicolon delimited file that you can import into Excel and filter. Happy patching.
Const KEY_QUERY_VALUE = &H0001
Const HKEY_LOCAL_MACHINE = &H80000002
VerifyQfeInstalled
Sub VerifyQfeInstalled()
Dim aHostArray, aQfeArray
Dim bQfeFound
Dim iIndex, iInnerIndex
Dim oWmi, oFs, oHostList, oLog, oQfeList, oQfeItem, colQfeItems, oRegistry
Dim sDate, sTime, sDateTime, sLog, sHostList, sQfeList, sHosts, sQfeContent, sServer, sQfeId, sQfeInfo, sHelpLink, sInstallDate
sDate = Date()
sDate = Replace(sDate, "/", "_")
sTime = Time()
sTime = Replace(sTime, ":", "_")
sTime = Replace(sTime, " ", "_")
sDateTime = sDate & "_" & sTime
sLog = "VerifyQfeInstalled_" & sDateTime & ".log"
Set oFs = CreateObject("Scripting.FileSystemObject") Set oLog = oFs.CreateTextFile(sLog)
'oLog.WriteLine("Created file [" & sLog & "] and opened it for WRITING") Wscript.StdOut.WriteLine "Processing started."
If Wscript.Arguments.Count < 2 Then
Wscript.StdOut.WriteLine "ERROR: You must supply a host list file and QFE list file!"
oLog.Close()
oFs.DeleteFile sLog, True
Set oLog = Nothing
Set oFs = Nothing
Wscript.Quit()
Else
sHostList = Trim(Wscript.Arguments(0))
sQfeList = Trim(Wscript.Arguments(1))
End If
If (oFs.FileExists(sHostList)) Then
'oLog.Write("Opening [" & sHostList & "] for READING...") Set oHostList = oFs.OpenTextFile(sHostList, 1, False, 0)
sHosts = oHostList.ReadAll
'oLog.Write("READ...") oHostList.Close()
Set oHostList = Nothing
'oLog.WriteLine("CLOSED") aHostArray = Split(sHosts, vbNewLine)
'oLog.WriteLine("Read [" & UBound(aHostArray) + 1 & "] items.") 'oLog.WriteBlankLines(1)
If (oFs.FileExists(sQfeList)) Then
'oLog.Write("Opening [" & sQfeList & "] for READING...") Set oQfeList = oFs.OpenTextFile(sQfeList, 1, False, 0)
sQfeContent = oQfeList.ReadAll
'oLog.Write("READ...") oQfeList.Close()
Set oQfeList = Nothing
'oLog.WriteLine("CLOSED") aQfeArray = Split(sQfeContent, vbNewLine)
'oLog.WriteLine("Read [" & UBound(aQfeArray) + 1 & "] items.") 'oLog.WriteBlankLines(1)
Else
Wscript.StdOut.WriteLine "ERROR: The file you specified for the QFE list does not exist!"
oLog.Close()
oFs.DeleteFile sLog, True
Set oLog = Nothing
Set oFs = Nothing
Wscript.Quit()
End If
Else
Wscript.StdOut.WriteLine "ERROR: The file you specified for the host list does not exist!"
oLog.Close()
oFs.DeleteFile sLog, True
Set oLog = Nothing
Set oFs = Nothing
Wscript.Quit()
End If
For iIndex = 0 To UBound(aHostArray)
sServer = Trim(aHostArray(iIndex))
Wscript.StdOut.WriteLine "Checking " & sServer
If (PingHost(sServer)) Then
Set oWmi = GetObject("winmgmts:{impersonationLevel=impersonate}!\\" & sServer & "\root\cimv2")
For iInnerIndex = 0 To UBound(aQfeArray)
bQfeFound = False
If (Left(Trim(aQfeArray(iInnerIndex)), 2) <> "KB") Then
sQfeId = "KB" & Trim(aQfeArray(iInnerIndex))
Else
sQfeId = Trim(aQfeArray(iInnerIndex))
End If
sHelpLink = GetHelpLink(sServer, sQfeId)
sInstallDate = GetSecondaryDate(sServer, sQfeId)
If (sInstallDate <> "") Then
sInstallDate = MakeFriendlyDate(sInstallDate)
End If
Set colQfeItems = oWmi.ExecQuery("SELECT * FROM Win32_QuickFixEngineering WHERE HotFixID='" &sQfeId & "'") If (colQfeItems.Count <> 0) Then
For Each oQfeItem in colQfeItems
bQfeFound = True
sQfeInfo = UCase(oQfeItem.CSName) & ";" & oQfeItem.Description & ";" & oQfeItem.HotFixID & ";" & oQfeItem.InstallDate & ";" & sInstallDate & ";" & oQfeItem.InstalledBy & ";" & bQfeFound & ";" & sHelpLink
oLog.WriteLine(sQfeInfo)
Next
Else
sQfeInfo = UCase(sServer) & ";;" & sQfeId & ";;;;" & bQfeFound & ";"
oLog.WriteLine(sQfeInfo)
End If
Next
Else
Wscript.StdOut.WriteLine "**WARNING** " & sServer & " is not pingable!"
'oLog.WriteLine("**WARNING** " & sServer & " is not pingable!") End If
Next
oLog.Close()
Wscript.StdOut.WriteLine "Done processing."
Wscript.StdOut.WriteLine "Log file is [" & sLog & "]."
Set oQfeItem = Nothing
Set colQfeItems = Nothing
Set oWmi = Nothing
Set oLog = Nothing
Set oFs = Nothing
End Sub
Function PingHost(ByVal sServer)
Dim oShell, oExec
Dim sResults
Dim bIsSuccess
bIsSuccess = False
Set oShell = CreateObject("WScript.Shell") Set oExec = oShell.Exec("ping.exe -n 2 -w 1000 " & sServer) sResults = LCase(oExec.StdOut.ReadAll)
If InStr(sResults, "reply from") Then
If InStr(sResults, "destination host unreachable") Then
'WScript.Echo "ERROR: Destination Unreachable!"
Else
bIsSuccess = True
'WScript.Echo "SUCCESS"
End If
Else
Wscript.StdOut.WriteLine "ERROR: Host Unreachable!"
End If
Set oExec = Nothing
Set oShell = Nothing
PingHost = bIsSuccess
End Function
Function MakeFriendlyDate(ByVal sDate)
Dim sYear, sMonth, sDay, sInstalledOn
sYear = Left(sDate, 4)
sMonth = Mid(sDate, 5, 2)
sDay = Left(sDate, 2)
sInstalledOn = sMonth & "/" & sDay & "/" & sYear
MakeFriendlyDate = sInstalledOn
End Function
Function GetHelpLink(ByVal sServer, ByVal sQfeId)
Dim oRegistry
Dim sKeyRoot, sKey, sValue, sHelpLink
Set oRegistry = GetObject("winmgmts:{impersonationLevel=impersonate}!\\" & sServer & "\root\default:StdRegProv") sKey = "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\" & sQfeId
sValue = "HelpLink"
oRegistry.GetStringValue HKEY_LOCAL_MACHINE, sKey, sValue, sHelpLink
Set oRegistry = Nothing
GetHelpLink = sHelpLink
End Function
Function GetSecondaryDate(ByVal sServer, ByVal sQfeId)
Dim oRegistry
Dim sKeyRoot, sKey, sValue, sInstallDate
Set oRegistry = GetObject("winmgmts:{impersonationLevel=impersonate}!\\" & sServer & "\root\default:StdRegProv") sKey = "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\" & sQfeId
sValue = "InstallDate"
oRegistry.GetStringValue HKEY_LOCAL_MACHINE, sKey, sValue, sInstallDate
Set oRegistry = Nothing
GetSecondaryDate = sInstallDate
End Function