' Author = H.S. (DevOps) ' This script is designed specifictly for Nagios ' More specifics can be found here: http://nagios-plugins.org/doc/guidelines.html#AEN78 ' This script will compare a ClickOnce installed 'Runtime process' with a source Application metadata file ' it will parse the application file using and compare using Binary. Do NOT bother or try to compare using the version numbers! ' Because the version numbers may not always be consistent. Because the version number can either be assigned at the File level or at the Assembly level. ' Hence the best way to verify the user is running the latest ClickOnce Application is to compare at the binary level. ' If Binaries do not match -> throw critical status, anything else status will be OK ' No Proc/instance Running Status =0 (status OK N/A) ' If instance found and binaries do not match = critical ' if instance found and binaries match = Status OK ' if Application metadata not found = Status Critical '''''''''''''''''''''''''''''''''''''''''''''''''''''''''' If wscript.arguments.count < 2 Then WScript.Echo "Required Arguments" WScript.Echo "1- Process Name (i.e. MyApp.exe)" WScript.Echo "2- Source Path To clickOnce Application MetaData (i.e. \\server\share\MyApp.application)" WScript.Echo "-------------------" wscript.quit End If dim wmi, ProcList, process, ProcessName, path, AppMetadataFilepath, ApplicationMetadataVer, StOutput, PerfData 'Used for debuging.... 'Wscript.echo "[Quering Process]=" & Wscript.Arguments.Unnamed.Item(0) 'Wscript.echo "[rc Path]=" & Wscript.Arguments.Unnamed.Item(1) StOutput = "[Proc to Query]=" & Wscript.Arguments.Unnamed.Item(0) StOutput = StOutput & ";" & chr(10) & "[src Path]=" & Wscript.Arguments.Unnamed.Item(1) 'Used for debuging. 'Wscript.echo StOutput outputCode = 0 ProcessName = lcase(Wscript.Arguments.Unnamed.Item(0)) AppMetadataFilepath = Wscript.Arguments.Unnamed.Item(1) Set objFSO = CreateObject("Scripting.FileSystemObject") function FileCompare(fileA,fileB) Const adTypeBinary = 1 dim sFileA, sFileB, bMatched, nSize, bufA, bufB,lengthA, lengthB Dim streamA,streamB FileCompare = null if vartype(fileA) <> vbString then on error resume next sFileA = fileA.path if err then exit function on error goto 0 else if not objFSO.fileexists(fileA) then exit function else sFileA = fileA end if end if if vartype(FileB) <> vbString then on error resume next sFileB = FileB.path if err then exit function on error goto 0 else if not objFSO.fileexists(FileB) then exit function else sFileB = FileB end if end if Set streamA = CreateObject("ADODB.Stream") Set streamB = CreateObject("ADODB.Stream") streamA.Type = adTypeBinary streamB.Type = adTypeBinary streamA.open streamB.open on error resume next streamA.loadfromfile sFileA if err then exit function streamB.loadfromfile sFileB if err then exit function on error goto 0 bMatched = true nSize = 2^15 '32K do until streamA.eos or streamB.eos bufA = streamA.read(nSize) bufb = streamB.read(nSize) lengthA = lenB(bufA) lengthB = lenB(bufB) if lengthA <> lengthB then bMatched = false exit do elseif MidB(bufA,1,lengthA) <> MidB(bufB,1,lengthB) then bMatched = false exit do end if loop if not (streamA.eos and streamB.eos) then bMatched = false end if streamA.close streamB.close FileCompare = bMatched end function ' Dim srcFileBin, ProcFileBin, ProcFileName, ProcDeployFilePath Dim FileBinMatch, AppMetaDataRootPath if objFSO.FileExists(AppMetadataFilepath) then AppMetaDataRootPath = objFSO.GetParentFolderName(AppMetadataFilepath) Set objXMLDoc = CreateObject("Microsoft.XMLDOM") objXMLDoc.async = False objXMLDoc.load(AppMetadataFilepath) 'Example "\\server\share\MyApp.application" Set Root = objXMLDoc.documentElement Set NodeList = Root.getElementsByTagName("assemblyIdentity") if (NodeList.Length > 0) then ApplicationMetadataVer = NodeList.Item(0).getAttribute("version") PerfData = "|'Src Version'=" & ApplicationMetadataVer end if Set NodeList = Root.getElementsByTagName("dependentAssembly") if (NodeList.Length > 0) then codebase = NodeList.Item(0).getAttribute("codebase") AppMetaDataRootPath = AppMetaDataRootPath & "\" & codebase 'this will have the full .manifest file AppMetaDataRootPath = objFSO.GetParentFolderName(AppMetaDataRootPath) 'we need to get the root path again from the version folder end if '############### if (NOT objFSO.FolderExists(AppMetaDataRootPath)) then StateDescription = "Error Src Application Path Not found:" & AppMetaDataRootPath outputStatus = "CRITICAL!" outputCode = 2 else set wmi = GetObject("winmgmts:{impersonationLevel=impersonate}!\\.\root\cimv2") 'set ProcList = wmi.ExecQuery("Select * from Win32_Process") set ProcList = wmi.ExecQuery("Select * from Win32_Process where name = '" & lcase(ProcessName) & "'") if (ProcList.Count <=0) or IsNull(ProcList) Then 'No Proc Found PerfData = PerfData & chr(10) & "|'Runtime Version'=0" StateDescription = "Proc Not Found, Not Running" outputStatus= "(N/A)-OK!" outputCode = 0 Else 'NOTE: There could be multiple instances of the same proc if multiple users/sessions are logged on, hence go through all of them for each process in ProcList path = process.ExecutablePath ProcFileName = objFSO.GetFileName(path) 'NOTE: We can't use FileVer since FileVer might be 1.0.0.0 'FileVer =objFSO.GetFileVersion(path) 'PerfData = PerfData & chr(10) & "|'Run Time Version'=" & FileVer ProcDeployFilePath = AppMetaDataRootPath & "\" & ProcFileName & ".deploy" 'this will be our Source to compare Binary with Proc file... if objFSO.FileExists(ProcDeployFilePath) then FileBinMatch = FileCompare (path,ProcDeployFilePath) 'NOTE: DO NOT try to compare using vesion numbers! 'because not every file version matches the assembly version. Some file versions are not changed they are left (1.0.0.0) 'the version in the .application metadata file refers to the file/build version 'comparing the file at the binary level is the best/solid solution! if FileBinMatch then PerfData = PerfData & chr(10) & "|'Binary Match'=" & ApplicationMetadataVer StateDescription = "Application running latest version" StateDescription = StateDescription & chr(10) & "[Latest]" & path outputStatus= "OK" outputCode= 0 else 'Wscript.Echo "Error: Application does not match!" PerfData = PerfData & chr(10) & "|'Binaries Not Match'=-1" StateDescription = "Error: Application does not match!" StateDescription = StateDescription & chr(10) & "[Not Latest]" & path outputStatus= "CRITICAL!" outputCode= 2 exit for 'Exit for loop when any of the proc are mismatched, regardless if there are multiple processes from different users. end if Else 'Wscript.Echo "Error: Src file not found!" StateDescription ="Error: Src file not found! " & ProcDeployFilePath outputStatus= "CRITICAL!" outputCode= 2 end if next End if end if else StateDescription = "Error Src Application file not found:" & AppMetadataFilepath outputStatus = "CRITICAL!" outputCode = 2 end if '(be nice) - Unload objects. if Not IsNull(objFSO) Then Set objFSO = Nothing if Not IsNull(Root) Then Set Root = Nothing if Not IsNull(NodeList) Then Set NodeList = Nothing Wscript.Echo "Status: " & outputStatus & " : " & StateDescription 'NOTE: PerfData must be AFTER the status, otherweise the status output is hidden/lost 'PerfData must start with pipe '|', and each perf data value must ba int, and each perf data must start in a new line chr(10) Wscript.Echo PerfData 'Quit with correct status (0=OK, 1=Warning, 2=Critical, 3=Unknown) wscript.quit(outputCode)