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.
Example usage: C:\>cscript verifyqfe.vbs servers.txt hotfixes.txt
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
oQfeList.Close()
Set oQfeList = Nothing
aQfeArray = Split(sQfeContent, vbNewLine)
'oLog.WriteLine("Read [" & UBound(aQfeArray) + 1 & "] items.")
Wscript.StdOut.WriteLine "ERROR: The file you specified for the QFE list does not exist!"
Wscript.StdOut.WriteLine "ERROR: The file you specified for the host list does not exist!"
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))
sQfeId = Trim(aQfeArray(iInnerIndex))
sHelpLink = GetHelpLink(sServer, sQfeId)
sInstallDate = GetSecondaryDate(sServer, sQfeId)
If (sInstallDate <> "") Then
sInstallDate = MakeFriendlyDate(sInstallDate)
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
sQfeInfo = UCase(sServer) & ";;" & sQfeId & ";;;;" & bQfeFound & ";"
Wscript.StdOut.WriteLine "**WARNING** " & sServer & " is not pingable!"
'oLog.WriteLine("**WARNING** " & sServer & " is not pingable!")
Wscript.StdOut.WriteLine "Done processing."
Wscript.StdOut.WriteLine "Log file is [" & sLog & "]."
Set oQfeItem = Nothing
Set colQfeItems = Nothing
Set oWmi = 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!"
bIsSuccess = True
'WScript.Echo "SUCCESS"
Wscript.StdOut.WriteLine "ERROR: Host Unreachable!"
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
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
Function GetSecondaryDate(ByVal sServer, ByVal sQfeId)
Dim sKeyRoot, sKey, sValue, sInstallDate
sValue = "InstallDate"
oRegistry.GetStringValue HKEY_LOCAL_MACHINE, sKey, sValue, sInstallDate
GetSecondaryDate = sInstallDate