'==========================================================================
'
' NAME: Install-Cylance-PROTECT-x64-2.1.1570.35-M-R1.vbs
'
' AUTHOR:  Max Erenburg
' DATE  : 01/02/2019
'
' COMMENT: This script will verify that Cylance PROTECT x64 is installed properly
' on all assets and DC's (at this time until EASE integration, if ever).
' 
' Uninstalling the Agent on the device does not remove the device from the Console. You must manually remove the device from the Console.
' If Prevent Service Shutdown from Device (Settings > Device Policy > Protection Settings) is enabled, either disable it in the policy or apply a different policy to the
' devices from which you want to uninstall the agent. Another method is to delete the device from the Console and then restart the device. This should unregister the
' device and allow you to uninstall the Agent.
'
' Update 02/15/2019 updated MDTDeployCheck call (to included clients; Server had gained this check with release of this Cylance version.), also added update GetOSInfo funtion.
' Update 02/16/2019 updated Alert formating & additional details
' Update 04/02/2019 update to new package version 2.0.1530.5
' Update 08/01/2019 update to new package version 2.0.1530.14 and Pilot package detection 2.0.1534.15 (added code to generate ticket with SD system): Mandatory Patch for Vuln.
' Update 08/05/2019 update to new package version 2.0.1534.15 (changed code to generate ticket as it@theorchard.com): Mandatory Patch for Vuln.
' Update 04/14/2020 update to new package version 2.0.1540.8 (Cylance is withholding the 2.1.1550.x CylancePROTECT Agent for Windows at this time still)
' Update 06/24/2020 update to new package version 2.1.1560.25
' Update 07/02/2020 update, changed check of driver from "CyProtectDrv64.sys" to "CylanceDrv64.sys"
' Update 08/20/2020 update to new package version 2.1.1564.39
' Update 09/30/2020 update to new package version 2.1.1564.40 Security hotfix and allow pilot of 1570.35
' Update 09/30/2020 update to new package version 2.1.1570.35
'==========================================================================

Option Explicit

Dim vDate, vTime, altTime, altDate, vDateTime
vDateTime = Now

Dim oShell, oFSO, oProcessEnv, sScript, PUBLICPROPERTY, sSVCName, iErrCode, sMsg
Dim sDebugBlat, sMSIDebugQA, sSuccess, sPilot, sTicket
Dim sMailbox1, sMailbox2, sMailbox3, sSD, sRelayHost, sFrom, sCN, sFQDN, sTry
Dim sSrcRoot, sSystem32, sLogDir, sScriptsDir, sEmailDir, sDebugLog
Dim sDomFQDN, sDN, sLib, sBlat, sEngKey
Dim sCoreBlat, sCoreBlat2, sCoreWMI, sCoreInfo, sCoreInfo2, sCoreInfo3, DebugLog
Dim ProductName, IsServer, IsServerCore, CBVer, CSDver, s64Bit, DC, sPBV, PBV, sPTV, PTV
Dim oWMI, sWMIFailed, sWMIMsgBody2
Dim LBT, iOSLang, sOSLangHex, sOSLanguage
Dim TZ, description, address, subnet, subnetmask
Dim iMSIVal, oMSIinstaller, AppName, MSIver, GUID, sAlreadyInst, sFound
Dim SystemUptimeMinutes, sReboot, sDrvName, sCacheName

Set oShell = CreateObject("WScript.Shell")
Set oFSO = CreateObject("Scripting.FileSystemObject")
Set oProcessEnv = oShell.Environment("PROCESS")

' Packager's defined variable's for MSI based installations
Const PRODUCTCODE = "{2E64FC5C-9286-4A31-916B-0D8AE4B22954}" ' Idiots use the same PRODUCTCODE for each new package.
Const DEPLOYNAME = "Cylance-PROTECT-x64-2.1.1570.35"
Const SIDE = "-M"
Const RELEASE = "-R1"
Const VERSION = "2.1.1570.35"
Const PILOT_VERSION = "" ' If no version is being piloted then set to either "" or Null
sScript = "Install-" & DEPLOYNAME & SIDE & RELEASE
PUBLICPROPERTY = "PIDKEY=gTrBGzValtmKp5Caj0QltB3x"
sSVCName = "CylanceSvc"
sDrvName = "CylanceDrv64.sys" ' was "CyProtectDrv64.sys"
sCacheName = "CylanceUD.cache" ' was "CyProtect.cache"

sDebugBlat = "False" ' Email if blat found missing and copied down local.
sMSIDebugQA = "False" ' QA Mode for MSI '(used to speed up large package testing by running "Notepad" instead of actual MSI/EXE to test pre/post functions
sSuccess = "False" ' Set to False if you dont want success emails. ' For enterprise deployment set to false, for one of installations leave as True
sPilot = "False" ' Set to False if you dont want attachment of debug file to success emails. ' Use for QA/UAT, once ready for Production deployments set to False
sTicket = "False"
sReboot = "No" ' If we require to forcefully reboot asset at some point

sMailbox1 = "merenburg@theorchard.com" : sMailbox2 = "" : sMailbox3 = "bwilliams@theorchard.com" : sSD = "techsupport@theorchard.com"
sRelayHost = "smtp-relay.gmail.com"
sFrom = "it"
sCN = oProcessEnv("COMPUTERNAME")
sFQDN = "@TheOrchard.com"
sTry = "2"

sSrcRoot = Replace(WScript.ScriptFullName, WScript.ScriptName, "")
sSystem32 = oProcessEnv("SYSTEMROOT") & "\system32\"
sLogDir = oProcessEnv("SYSTEMROOT") & "\EngLogs"
sScriptsDir = sLogDir & "\Scripts"
sEmailDir = sLogDir & "\Email"
sDebugLog = sScriptsDir & "\" & sScript & ".log"

sBlat = sSystem32 & "blat.exe"
sEngKey = "HKLM\SOFTWARE\ORCDENG\"

' Initialize WMI connection to cimv2
On Error Resume Next 
Set oWMI = GetObject("winmgmts:\\.\root\cimv2")
If Err.Number <> 0 Then
	iErrCode = Err.Number
	sWMIFailed = "Failed"
	sWMIMsgBody2 = Now & "__ Script was unable to successfully connect to cimv2, Error code: " & iErrCode & ", installations are dependent on WMI being healthy, cannot proceed."
End If
On Error GoTo 0

sMsg = "Scriptname: " & WScript.ScriptName
sMsg = sMsg & "|ScriptPath: " & WScript.ScriptFullName

' Get Domain Name we are working with so we dont have to have additional copies of same script per AD Forest/Domain.
Call GetDomainName
sLib = "\\" & sDomFQDN & "\netlogon\lib\"

' Ensure email alerts are available
Call CopyBlat

' Get what OS, CSD version are we working with
Call GetOSInfo

' Get OS Install Date
Call GetInstallDate

' Get OS Locale (Get Locale information)
Call GetOSLocale

' Get Time Zone description
Call GetTZ

' Get Nic information we are working with
Call GetNetworkInterface

' Get Manufacturer/Model info on the device
Call GetDeviceInfo

' Create directory(s) for local centralized logging
Call CreateLogDirs

' Create debug log or append to log if this script has been run before
Call CreateDebugLog("FirstCheck")

If sWMIFailed = "Failed" Then 'fail with hard error as we need WMI up and running (health to perform installs, etc..)
	If Not IsObject(DebugLog) Then Call CreateDebugLog("WriteLog")
	WSHLog "CriticalFailure", sWMIMsgBody2
End If

' Check if DC Function
Call IsDC

If s64Bit = "0" Then WSHLog "CriticalFailure", "This script does not support 32 bit OS."

' Check if product is already Installed
Call ChkPkgAlreadyInstalled

If sAlreadyInst = "No" Then
	If Not IsObject(DebugLog) Then Call CreateDebugLog("WriteLog")
	DebugLog.WriteLine(Now & "__ Product: " & PRODUCTCODE & " was not found installed.")
	' Setup package configuration based on OS
	Call MSIPreInst

	' Install MSI package
	Call MSIPkgInstall("/qb!")
	Call Tattoo("New")

	' Check AV QualityCompat key
	Call PostInst
	
	DebugLog.WriteLine(Now & "__ We're going to sleep now for 20 seconds to allow Cylance to pull down policy before we reboot.")
	WScript.Sleep 20000
	sReboot = "Yes"
ElseIf sAlreadyInst = "Upgrade" Then
	' Upgrades are pushed via cloud, we dont handle via this script, however we do verify if not on current approved PROD version.
	sTicket = "True"

ElseIf sAlreadyInst = "Reinstall" Then
	If Not IsObject(DebugLog) Then Call CreateDebugLog("WriteLog")
	DebugLog.WriteLine(Now & "__ Proceeding to reinstall.")
	' Setup package configuration based on OS
	Call MSIPreInst

	' Install package
	Call MSIPkgInstall("/qb!")
	Call Tattoo("Reinstall")

	' Check AV QualityCompat key
	Call PostInst
ElseIf sAlreadyInst = "Yes" Then' Going to run a health check on installation
	Call AppHealthCheck
	If sAlreadyInst = "Corrupted" Then
		' Protected install (via policy), can't automate removal/re-install
		sTicket = "True"
	ElseIf Not(IsNull(sAlreadyInst) Or IsEmpty(sAlreadyInst) Or sAlreadyInst = "Yes") Then
		If Not IsObject(DebugLog) Then Call CreateDebugLog("WriteLog")
		DebugLog.WriteLine(Now & "__ Found value of " & sAlreadyInst & " after AppHealthCheck, not accounted for. Use this asset for reference to the condition.")
		iErrCode = "999"
	Else
		If IsObject(DebugLog) Then DebugLog.WriteLine(Now & "__ AppHealthCheck function passed, nothing to do.")
	End If
	' Check AV QualityCompat key
	Call PostInst
	
	' MDT OS Deployment Check
	Call MDTDeployCheck
Else
	If Not IsObject(DebugLog) Then Call CreateDebugLog("WriteLog")
	DebugLog.WriteLine(Now & "__ Found no value for sAlreadyInst, must be some condition not accounted for. Use this asset for reference to the condition.")
	iErrCode = "999"
End If

' Log/email exit code, cleanup and exit
If Not(IsNull(iMSIVal) Or IsEmpty(iMSIVal)) Then
	Call Finalize(iMSIVal, "MSI")
ElseIf Not (IsNull(iErrCode) Or IsEmpty(iErrCode)) Then
	Call Finalize(iErrCode, "ERROR")
Else
	Call Finalize(Err.Number, NULL)
End If


' ------   Packager's defined Functions/Subs section    -------

' See is this asset is being deployed by MDT.
Function MDTDeployCheck
	Const ForReading = 1 : Const ForWriting = 2 : Const ForAppending = 8
	On Error Resume Next
	Dim sPBD, PBD, sPBN, PBN, sPBT, PBT, sPBV, PBV, dtmInstallDate, colOS, oOS, OSInstDate
	Dim sDeployDir, sLog, DebugLog2, iErrDesc, sLT, sCount, sMINIScripts, sCMD, iVal
	sEngKey = "HKLM\SOFTWARE\ORCDENG\"
	sPBD = sEngKey & "Build\BuildDate"
	sPBN = sEngKey & "Build\BuildName"	
	sPBT = sEngKey & "Build\BuildTime"
	sPBV = sEngKey & "Build\BuildVersion"
	PBD = GetRegData(sPBD, "{default}")
	PBN = GetRegData(sPBN, "{default}")
	PBT = GetRegData(sPBT, "{default}")
	PBV = GetRegData(sPBV, "{default}")
	If PBD = "{default}" OR PBD = "" Or IsEmpty(PBD) Or IsNull(PBD) Then
		Exit Function
	ElseIf PBN = "{default}" OR PBN = "" Or IsEmpty(PBN) Or IsNull(PBN) Then
		Exit Function
	ElseIf PBT = "{default}" OR PBT = "" Or IsEmpty(PBT) Or IsNull(PBT) Then
		Exit Function
	ElseIf PBV = "{default}" OR PBV = "" Or IsEmpty(PBV) Or IsNull(PBV) Then
		Exit Function
	Else
		Call Stamp
		Set dtmInstallDate = CreateObject("WbemScripting.SWbemDateTime")
		Set colOS = oWMI.ExecQuery("SELECT InstallDate FROM Win32_OperatingSystem")
		For Each oOS in colOS
			dtmInstallDate.Value = oOS.InstallDate
			OSInstDate = dtmInstallDate.Month & "/" & dtmInstallDate.Day & "/" & dtmInstallDate.Year
		Next
		If OSInstDate = vDate Then
			sDeployDir = sLogDir & "\Deploy"
			If oFSO.FolderExists(sDeployDir) Then
				sLT = oProcessEnv("SystemDrive") & "\MININT\SCRIPTS\LiteTouch.wsf"
				If oFSO.FileExists(sLT) Then
					sLog = sDeployDir & "\Deploy-MDT-Cont-After-JoinDom.log"
					If NOT oFSO.FileExists(sLog) Then
						Set DebugLog2 = oFSO.OpenTextFile(sLog, ForWriting, True)
						If Err.Number <> 0 Then
							iErrCode = Err.Number
							iErrDesc = CStr(Err.Number) & " " & Err.Description & "."
							WSHLog "CriticalFailure", "Attempt to create Debuglog: " &  sLog & ", failed. Error code: " & iErrDesc
						End If
					Else ' Append to existing log
						Set DebugLog2 = oFSO.OpenTextFile(sLog, ForAppending, True)
						If Err.Number <> 0 Then
							iErrCode = Err.Number
							iErrDesc = CStr(Err.Number) & " " & Err.Description & "."
							WSHLog "CriticalFailure", "Attempt to append to Debuglog: " & sLog & ", failed. Error code: " & iErrDesc
						Else
							DebugLog2.WriteLine(" ")
						End If
					End If
					DebugLog2.WriteLine(Now & "__ Continuing MDT deployment on " & oProcessEnv("COMPUTERNAME"))
					DebugLog2.WriteLine(Now & "__ Running on Build Version: " & PBV)
					DebugLog2.WriteLine(Now & "__ Found " & sLT & " exists.")
					sMINIScripts = oProcessEnv("SystemDrive") & "\MININT\SCRIPTS"
					With oFSO.GetFolder(sMINIScripts)
						sCount = .Files.Count
						DebugLog2.WriteLine(Now & "__ Found " & sCount & " files exist in " & sMINIScripts)
						If sCount > 30 Then
							DebugLog2.WriteLine(Now & "__ Proceeding to launch LiteTouch.wsf to continue deployment TS.")
							If IsObject(DebugLog2) Then DebugLog2.Close
							sCMD = "wscript.exe " & sLT & " /start"
							If Not IsObject(DebugLog) Then Call CreateDebugLog("WriteLog")
							DebugLog.WriteLine(Now & "__ Found MDT Deployment TS pending continuation, issuing commandline: " & sCMD & " .")
							iVal = oShell.Run(sCMD, 1, True)
							If iVal <> 0 Then
								iErrCode = iVal
								iErrDesc = CStr(iVal) & " " & Err.Description & "."
								DebugLog.WriteLine(Now & "__ Execution of " & sCMD & " FAILED, result code: " & iErrDesc)
								WSHLog "CriticalFailure", "Execution of " & sCMD & " FAILED, result code: " & iErrDesc
							End If
							iVal = Null
							If Not IsServerCore = "True" Then
								sCMD = "wscript.exe " & sLT
								oShell.Run sCMD, 1, False
							End If
						Else
							DebugLog2.WriteLine(Now & "__ Which is less than 30 files we expected at least. This will require manually looking into. Script Exiting.")
							' Warning email
							If IsObject(DebugLog2) Then DebugLog2.Close
							Email "Warning", "Found MDT deployment staged on asset but with " & sCount & " files only in " & sMINIScripts & " " & sMsg
						End If
					End With
				Else
					'If Not IsObject(DebugLog) Then Call CreateDebugLog("WriteLog")
					'DebugLog.WriteLine(Now & "__ Found " & sDeployDir & " exists but " & sLT & " does not.")
					Call GetWhoIsRunningScript
					Email "Warning", "Found " & sDeployDir & " exists but " & sLT & " does not." & "||" & sMsg
				End If
			Else
				' Asset already deployed
			End If
		Else
			Exit Function
		End If
	End If
	On Error GoTo 0
End Function

' Application/Installation Health Check Function
Function AppHealthCheck
	On Error Resume Next
	'check if service exists and base on up time of asset if process is running or not
	Dim sPFN, sProc, sPD, sFCount, sMaxSvcUptime, ChkSVC, ChkProc, sVerbiage
	sPFN = "Cylance PROTECT x64"
	sPD = oProcessEnv("ProgramFiles") & "\Cylance\Desktop"
	sProc = "CylanceSvc.exe"
	sFCount = 55
	sMaxSvcUptime = 1 ' minute(s)
	ChkSVC = CheckService(sSVCName, "NULL", "NULL", "NoCheck", "NULL", "Required")
	If ChkSVC = "DoesntExist" Then
		If Not IsObject(DebugLog) Then Call CreateDebugLog("WriteLog")
		DebugLog.WriteLine(Now & "__ While performing an installation health check, we found that the " & sPFN & " msi installation is registered, but the service " & sSVCName & " was found to be missing.")
		sAlreadyInst = "Corrupted"
		'Call CheckforRemnants
	Else
		ChkProc = CheckProcess("Exact", sProc, Null, "Check", Null)
		If ChkProc = "notrunning" Then
			Call GetUpTime("n") ' in minutes
			If SystemUptimeMinutes < sMaxSvcUptime Then ' Just booted up
				If IsObject(DebugLog) Then DebugLog.WriteLine(Now & "__ " & sSVCName & " process was not found running but since the uptime is only " & SystemUptimeMinutes & " min(s). We expected it to be running within an uptime of " & sMaxSvcUptime & " min(s)., so we know it hasn't had a chance to start possibly. Will perform a quick file sanity check.")
				WSHLog "Information", sSVCName & " process was not found running but since the uptime is only " & SystemUptimeMinutes & " min(s). We expected it to be running within an uptime of " & sMaxSvcUptime & " min(s)., so we know it hasn't had a chance to start possibly. Will perform a quick file sanity check."
				Call SanityCheck
			Else
				If Not IsObject(DebugLog) Then Call CreateDebugLog("WriteLog")
				DebugLog.WriteLine(Now & "__ Found system uptime to be " & SystemUptimeMinutes & ", but service was found not running. will attempt to start it.")
				ChkSVC = CheckService(sSVCName, "Auto", "Automatic", "Running", "NULL", "Required")
				If ChkSVC = "FailedtostartService" Then
					Dim sExePath, ExePathKEY
					sExePath = "HKLM\System\CurrentControlSet\Services\" & sSVCName & "\ImagePath"
					ExePathKEY = GetRegData(sExePath, "{default}")
					If ExePathKEY = "{default}" Then
						DebugLog.WriteLine(Now & "__ Reg key " & sExePath & " does not exist. Sending email to consider performing a reinstall to this condition in the future.")
						'sAlreadyInst = "Reinstall"
						WSHLog "CriticalFailure", "Found " & sPFN & " registered as installed, service was found to exist but not running, failed to start. System uptime is " & SystemUptimeMinutes & ". Executable path not found in registry." 
					Else
						DebugLog.WriteLine(Now & "__ Found service registry key with path to executable to be " & ExePathKEY & ". Going to check file existence and version, etc.")
						If Not InStr(1, ExePathKEY, Chr(34), 1) = 0 Then
							Dim oSplit, oExePath : oSplit = Split(ExePathKEY, Chr(34), 2, 1)
							oExePath = Split(oSplit, (1), Chr(34), 2, 1)
							ExePathKEY = oExePath(0)
						End If
						If Not oFSO.FileExists(ExePathKEY) Then
							DebugLog.WriteLine(Now & "__ File in service image path: " & ExePathKEY & " does not exist on file system. Sending email to consider performing a reinstall to this condition in the future.")
							'sAlreadyInst = "Reinstall"
							WSHLog "CriticalFailure", "Found " & sPFN & " registered as installed, service was found to exist but not running, failed to start. System uptime is " & SystemUptimeMinutes & ". File in service image path: " & ExePathKEY & " does not exist on file system." 
						Else
							DebugLog.WriteLine(Now & "__ Found file in service image path: " & ExePathKEY & " exists on file system. Going to check file counts in " & sPFN & " xagt directory.")
							Dim sCheckPath1, f, fc
							sCheckPath1 = sPD
							If Not oFSO.FolderExists(sCheckPath1) Then
								DebugLog.WriteLine(Now & "__ Path " & sCheckPath1 & " not found to exist on file system. Sending email to consider performing a reinstall to this condition in the future.")
								'sAlreadyInst = "Reinstall"
								WSHLog "CriticalFailure", "Found " & sPFN & " registered as installed, service was found to exist but not running, failed to start. System uptime is " & SystemUptimeMinutes & ". Path " & sCheckPath1 & " not found to exist on file system." 
							Else
								Set f = oFSO.GetFolder(sCheckPath1)
								Set fc = f.Files
								If fc.Count < sFCount Then
									DebugLog.WriteLine(Now & "__ Found " & fc.Count & " files exist in " & sCheckPath1 & ". Should contain " & sFCount & " files. Sending email to consider performing a reinstall to this condition in the future.")
									'sAlreadyInst = "Reinstall"
									WSHLog "CriticalFailure", "Found " & sPFN & " registered as installed, service was found to exist but not running, failed to start. System uptime is " & SystemUptimeMinutes & ". Found " & fc.Count & " files exist in " & sCheckPath1 & ". Should contain " & sFCount & " files." 
								Else
									DebugLog.WriteLine(Now & "__ Found " & sCheckPath1 & " directory exists with " & fc.Count & " files in it.")
									'sAlreadyInst = "Reinstall"
									WSHLog "CriticalFailure", "Found " & sPFN & " registered as installed, service was found to exist but not running, failed to start. System uptime is " & SystemUptimeMinutes & ". Found " & sCheckPath1 & " directory exists with " & fc.Count & " files in it. We are not quite sure why the service is not starting." 								
								End If
							End If
						End If
					End If
				End If
			End If			
		End If
		Call SanityCheck
	End If
	If sAlreadyInst = "Corrupted" Then
		sVerbiage = "Cylance was found to be corrupted, this will require a manual visit to remediate (check complete log). As prior uninstall key is lost."
		DebugLog.WriteLine(Now & "__ " & sVerbiage)
		sMsg = sVerbiage & "||" & sMsg		
	End If
	On Error GoTo 0
End Function

' Various sanity checks for health of installation
Function SanityCheck
	On Error Resume Next
	Dim sPFN, sPD
	sPFN = "Cylance PROTECT x64"
	sPD = oProcessEnv("ProgramFiles") & "\Cylance\"

	Dim sHKLMSoft, HKLMSoftKEY, sCritical,	sCriticalFile
	sHKLMSoft = "HKLM\SOFTWARE\Cylance\Desktop\SerialNumber"
	HKLMSoftKEY = GetRegBinary(sHKLMSoft, "{default}")
	If HKLMSoftKEY = "{default}" Then
		If Not IsObject(DebugLog) Then Call CreateDebugLog("WriteLog")
		DebugLog.WriteLine(Now & "__ Reg key " & sHKLMSoft & " does not exist.")
		'sAlreadyInst = "Reinstall" ' future force resinstall if we see these conditions exist.
		sCritical = "Yes"
	End If
	sHKLMSoft = "HKLM\SOFTWARE\Cylance\Desktop\Zone"
	HKLMSoftKEY = GetRegData(sHKLMSoft, "{default}")
	If Not HKLMSoftKEY = "{default}" Then
		If Not IsObject(DebugLog) Then Call CreateDebugLog("WriteLog")
		DebugLog.WriteLine(Now & "__ Found reg key " & sHKLMSoft & " exist, which is indicative of an unlicensed installation.")
		'sAlreadyInst = "Reinstall" ' future force resinstall if we see these conditions exist.
		sCritical = "Yes"
	End If
	If sCritical = "Yes" Then
		DebugLog.WriteLine(Now & "__ Found issue with " & sPFN & " HKLM Software key.")
	End If

	If Not oFSO.FileExists(oProcessEnv("SYSTEMROOT") & "\" & sCacheName) Then
		If Not IsObject(DebugLog) Then Call CreateDebugLog("WriteLog")
		DebugLog.WriteLine(Now & "__ File " & oProcessEnv("SYSTEMROOT") & "\" & sCacheName & " not found to exist on file system. Sending email to consider performing a reinstall to this condition in the future.")
		'sAlreadyInst = "Reinstall" ' future force resinstall if we see these conditions exist.
		sCriticalFile = "Yes"
	End If
	If Not oFSO.FileExists(sSystem32 & "drivers\" & sDrvName) Then
		If Not IsObject(DebugLog) Then Call CreateDebugLog("WriteLog")
		DebugLog.WriteLine(Now & "__ File " & sSystem32 & "drivers\" & sDrvName & " not found to exist on file system. Which is the Cylance Protect Driver. Sending email to consider performing a reinstall to this condition in the future.")
		sAlreadyInst = "Corrupted"
	End If
	If sCriticalFile = "Yes" Then
		DebugLog.WriteLine(Now & "__ Found issue with " & sPFN & " installation configuration file(s).")
	End If

	Dim sCheckPath1, sCheckPath2, f, fc
	sCheckPath1 = oProcessEnv("ProgramData") & "\Cylance\Desktop"  ' 2 .db files. Check if Quarantine is corrupt C:\ProgramData\Cylance\Desktop\chp.db.01
	sCheckPath2 = oProcessEnv("ProgramFiles") & "\Cylance\Desktop" ' at least 55 files for healthly
	If Not oFSO.FolderExists(sCheckPath1) Then
		If Not IsObject(DebugLog) Then Call CreateDebugLog("WriteLog")
		DebugLog.WriteLine(Now & "__ Directory " & sCheckPath1 & " not found to exist on file system.")
		sAlreadyInst = "Corrupted"
	Else
		If oFSO.FileExists(sCheckPath1 & "\chp.db.01") Then
			If Not IsObject(DebugLog) Then Call CreateDebugLog("WriteLog")
			DebugLog.WriteLine(Now & "__ Found " & sCheckPath1 & "\chp.db.01 files exist in " & sCheckPath1 & ", per Cylance this file exists when Quarantine is corrupt.")
			sAlreadyInst = "Corrupted"
		Else		
			Set f = oFSO.GetFolder(sCheckPath1)
			Set fc = f.Files
			If fc.Count < 2 Then
				If Not IsObject(DebugLog) Then Call CreateDebugLog("WriteLog")
				DebugLog.WriteLine(Now & "__ Found " & fc.Count & " files exist in " & sCheckPath1 & ", when there should be at least two (.db files).")
				sAlreadyInst = "Corrupted"
			Else
				If IsObject(DebugLog) Then  DebugLog.WriteLine(Now & "__ Found " & sCheckPath1 & " directory exists and contains " & fc.Count & " file(s).")
			End If
		End If
	End If
	If Not oFSO.FolderExists(sCheckPath2) Then
		If Not IsObject(DebugLog) Then Call CreateDebugLog("WriteLog")
		DebugLog.WriteLine(Now & "__ Directory " & sCheckPath2 & " not found to exist on file system.")
		sAlreadyInst = "Corrupted"
	Else
		Set f = oFSO.GetFolder(sCheckPath2)
		Set fc = f.Files
		If fc.Count < 55 Then
			If Not IsObject(DebugLog) Then Call CreateDebugLog("WriteLog")
			DebugLog.WriteLine(Now & "__ Found " & fc.Count & " files exist in " & sCheckPath2 & ", when there should be at least 55.")
			'sAlreadyInst = "Reinstall" ' future force resinstall if we see these conditions exist.
			sCriticalFile = "Yes"
		Else
			If IsObject(DebugLog) Then  DebugLog.WriteLine(Now & "__ Found " & sCheckPath2 & " directory exists and contains " & fc.Count & " file(s).")
		End If
	End If
	If (sCritical = "Yes" AND sCriticalFile = "Yes") Then
		WSHLog "CriticalFailure", "Found issue with both " & sPFN & " HKLM Software key and installtion configuration file(s), see attached log." 
	ElseIf sCritical = "Yes" Then
		WSHLog "CriticalFailure", "Found issue with " & sPFN & " HKLM Software key, see attached log." 
	ElseIf sCriticalFile = "Yes" Then
		WSHLog "CriticalFailure", "Found issue with " & sPFN & " installation configuration file(s), see attached log." 
	End If
	On Error GoTo 0
End Function

' Pre installtion Function
Function MSIPreInst
	If IsServer = "True" Then
		If IsServerCore = "True" Then PUBLICPROPERTY = PUBLICPROPERTY & " LAUNCHAPP=0" ' Dont enable Cylance UI (systray) on server core
		If InStr(UCase(ProductName), "2008") Or InStr(UCase(ProductName), UCase("2012")) Then
			PUBLICPROPERTY = PUBLICPROPERTY & " REGWSC=1" ' (1: Indicates that CylancePROTECT is registered with Windows as an antivirus program.) As we are not running Winows Defender ATP 
		Else ' Need to check If Windows Defender exists on Server 2016
			If Not IsObject(DebugLog) Then Call CreateDebugLog("WriteLog")
			DebugLog.WriteLine(Now & "__ Found OS: " & ProductName & ", " & CSDver & ". Script does not have this OS accounted for.")
			WSHLog "CriticalFailure", "Found OS: " & ProductName & ", " & CSDver & ". Script does not have this OS accounted for."
		End If
	Else
		If InStr(UCase(ProductName), "7") Then
			PUBLICPROPERTY = PUBLICPROPERTY & " REGWSC=1" ' (1: Indicates that CylancePROTECT is registered with Windows as an antivirus program.) As we are not running Winows Defender ATP 			
		ElseIf InStr(UCase(ProductName), "10") Then
			PUBLICPROPERTY = PUBLICPROPERTY & " REGWSC=0" ' (0: Indicates that CylancePROTECT is not registered with Windows as an anti-virus program. Allows CylancePROTECT and Windows Defender to run at the same time on the device.)
		Else
			If Not IsObject(DebugLog) Then Call CreateDebugLog("WriteLog")
			DebugLog.WriteLine(Now & "__ Found OS: " & ProductName & ", " & CSDver & ". Script does not have this OS accounted for.")
			WSHLog "CriticalFailure", "Found OS: " & ProductName & ", " & CSDver & ". Script does not have this OS accounted for."
		End If
	End If
End Function

' MSI package Installation Function
Function MSIPkgInstall(sDisplayOptions)
	Dim sMSIFile, sMSTFile, MSICMDLINE, sMsiLog, iInstVal
	sMSIFile = sSrcRoot & DEPLOYNAME & SIDE & RELEASE & ".msi"
	If NOT oFSO.FileExists(sMSIFile) Then
		iErrCode = "53"
		DebugLog.WriteLine(Now & "__ MSI specified to be installed in wrapper does not exist in source: " & sMSIFile)
		WSHLog "CriticalFailure", "MSI specified to be installed in wrapper does not exist in source: " & sMSIFile
	End If
	
	If oFSO.FileExists(sSrcRoot & DEPLOYNAME & SIDE & RELEASE & ".mst") Then
		sMSTFile = sSrcRoot & DEPLOYNAME & SIDE & RELEASE & ".mst"
	Else
		sMSTFile = Null
	End If
	sPilot = "True"
	Call Stamp
	If Not IsNull(sMSTFile) Then
		sMsiLog = Chr(34) & sScriptsDir & "\" & sScript & "-MSI_MST-" & altDate & "-" & altTime & ".log" & Chr(34)
		If PUBLICPROPERTY <> "" Then
			MSICMDLINE = "msiexec.exe REBOOT=ReallySuppress " & PUBLICPROPERTY & " TRANSFORMS=" & Chr(34) & sMSTFile & Chr(34) & " /i " & chr(34) & sMSIFile & chr(34) & " " & sDisplayOptions & " /L*v " & sMsiLog
		Else
			MSICMDLINE = "msiexec.exe REBOOT=ReallySuppress TRANSFORMS=" & Chr(34) & sMSTFile & Chr(34) & " /i " & chr(34) & sMSIFile & chr(34) & " " & sDisplayOptions & " /L*v " & sMsiLog
		End If
	Else
		sMsiLog = Chr(34) & sScriptsDir & "\" & sScript & "-MSI-" & altDate & "-" & altTime & ".log" & Chr(34)
		If PUBLICPROPERTY <> "" Then
			MSICMDLINE = "msiexec.exe REBOOT=ReallySuppress " & PUBLICPROPERTY & " /i " & chr(34) & sMSIFile & chr(34) & " " & sDisplayOptions & " /L*v " & sMsiLog
		Else
			MSICMDLINE = "msiexec.exe REBOOT=ReallySuppress /i " & chr(34) & sMSIFile & chr(34) & " " & sDisplayOptions & " /L*v " & sMsiLog
		End If
	End If

	DebugLog.WriteLine(Now & "__ Issuing installation commandline: " & MSICMDLINE)
	If sMSIDebugQA = "False" Then
		oProcessEnv("SEE_MASK_NOZONECHECKS") = 1
		iInstVal = oShell.Run(MSICMDLINE, 1, True)
		iMSIVal = iInstVal
		If iMSIVal = 0 Or iMSIVal = 3010 Then
			DebugLog.WriteLine(Now & "__ MSI installation completed, result code: " & iMSIVal)
		Else
			DebugLog.WriteLine(Now & "__ MSI installation FAILED, result code: " & iMSIVal)
			oProcessEnv.Remove("SEE_MASK_NOZONECHECKS")
			WSHLog "CriticalFailure", "MSI installation FAILED, result code: " & iMSIVal
		End If
	ElseIf sMSIDebugQA = "True" Then
		Dim sDebugTest
		sDebugTest = "NOTEPAD.EXE"
		iInstVal = oShell.Run(sDebugTest, 1, True)
	End If
End Function

' Keep track of Install/reinstall/upgrades
Function Tattoo(sPriorState)
	On Error Resume Next
	Dim sSetDate, sSetTime, sQSD, sQST, sQIC, QICKey, iErrDesc, sCount
	Call Stamp : sSetDate = vDate : sSetTime = vTime		
	sQSD = sEngKey & "Scripts\Cy\LastInstDate"
	sQST = sEngKey & "Scripts\Cy\LastInstTime"
	oShell.RegWrite sQSD, sSetDate, "REG_SZ"
	If Err.Number = 0 Then
		DebugLog.WriteLine(Now & "__ Created reg key " & sQSD & " with value of " & sSetDate)
	Else
		iErrCode = Err.Number
		iErrDesc = CStr(Err.Number) & " " & Err.Description & "."
		Call RegAlert(sQSD, sSetDate, iErrDesc)
	End If
	oShell.RegWrite sQST, sSetTime, "REG_SZ"
	If Err.Number = 0 Then
		DebugLog.WriteLine(Now & "__ Created reg key " & sQST & " with value of " & sSetTime)
	Else
		iErrCode = Err.Number
		iErrDesc = CStr(Err.Number) & " " & Err.Description & "."
		Call RegAlert(sQST, sSetTime, iErrDesc)
	End If
	sQIC = sEngKey & "Scripts\Cy\InstCount"
	QICKey = GetRegData(sQIC, "{default}")
	If QICKey = "{default}" Then
		If sPriorState = "Reinstall" Then
			sCount = "2"
		Else
			sCount = "1"
		End If
	Else
		sCount = QICKey + 1
	End If
	oShell.RegWrite sQIC, sCount, "REG_SZ"
	If Err.Number = 0 Then
		DebugLog.WriteLine(Now & "__ Created reg key " & sQIC & " with value of " & sCount)
	Else
		iErrCode = Err.Number
		iErrDesc = CStr(Err.Number) & " " & Err.Description & "."
		Call RegAlert(sQIC, sCount, iErrDesc)
	End If
	If sPriorState = "Upgrade" Then
		Dim sQUC, QUCKey
		sQUC = sEngKey & "Scripts\Cy\UpGradeCount"
		QUCKey = GetRegData(sQUC, "{default}")
		If QUCKey = "{default}" Then
			sCount = "1"
		Else
			sCount = QUCKey + 1
		End If
		oShell.RegWrite sQUC, sCount, "REG_SZ"
		If Err.Number = 0 Then
			DebugLog.WriteLine(Now & "__ Created reg key " & sQUC & " with value of " & sCount)
		Else
			iErrCode = Err.Number
			iErrDesc = CStr(Err.Number) & " " & Err.Description & "."
			Call RegAlert(sQUC, sCount, iErrDesc)
		End If
	End If
	On Error GoTo 0
End Function

' Log failed registry key/value creation function
Sub RegAlert(sRegPath, sValue, iErrDesc)
	DebugLog.WriteLine(Now & "__ Failed to create reg key " & sRegPath & " with value of " & sValue & ", Error code: " & iErrDesc)
	WSHLog "CriticalFailure", "Failed to create reg key " & sRegPath & " with value of " & sValue & ", Error code: " & iErrDesc
End Sub

'Check AV QualityCompat key function
Function PostInst
	On Error Resume Next
	Dim sQComp, QCompKEY, iErrDesc
	sQComp = "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\QualityCompat\cadca5fe-87d3-4b96-b7fb-a231484277cc"
	QCompKEY = GetRegData(sQComp, "{default}")
	If QCompKEY = "{default}" Then
		If Not IsObject(DebugLog) Then Call CreateDebugLog("WriteLog")
		DebugLog.WriteLine(Now & "__ Reg key " & sQComp & " does not exist. Proceeding to create key/value.")
		oShell.RegWrite sQComp, 0, "REG_DWORD"
		If Err.Number = 0 Then
			DebugLog.WriteLine(Now & "__ Created reg key " & sQComp & " with value of 0.")
			sPilot = "True"
		Else
			iErrCode = Err.Number
			iErrDesc = CStr(Err.Number) & " " & Err.Description & "."
			Call RegAlert(sQComp, 0, iErrDesc)
		End If
	Else
		If IsObject(DebugLog) Then DebugLog.WriteLine(Now & "__ Found reg key " & sQComp & " exists.")
	End If
		On Error GoTo 0
End Function

' Check if msi package already installed
Function ChkPkgAlreadyInstalled
	Dim sUninst, UninstKEY, COMP_VER, sVerbiage1, sVerbiage2
	Call GetMSIInfo("GUID", PRODUCTCODE, Null, Null) ' Idiots use the same PRODUCTCODE for each new package so we can go by PRODUCTCODE alone.
	If Not(IsNull(GUID) Or IsEmpty(GUID)) Then
		sFound = Now & "__ Found " & AppName & " v" & MSIver & " already installed."
		sUninst = "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\" & PRODUCTCODE & "\DisplayVersion"
		UninstKEY = GetRegData(sUninst, "{default}")
		If UninstKEY = "{default}" Then
			If Not IsObject(DebugLog) Then Call CreateDebugLog("WriteLog")
			DebugLog.WriteLine(Now & "__ Reg key " & sUninst & " does not exist, we have no real idea what version is installed.")
			sAlreadyInst = "Reinstall"
		Else
			If Not (IsEmpty(PILOT_VERSION) Or IsNull(PILOT_VERSION) OR Len(PILOT_VERSION & "") = 0) Then
				'If Not (UninstKEY = PILOT_VERSION) Then
				If UninstKEY = PILOT_VERSION Then
					sAlreadyInst = "Yes" ' Pilot version found installed
					Exit Function
				End If
			End If
			COMP_VER = CompareVersions(UninstKEY, VERSION)
			If COMP_VER = -1 Then
				If Not IsObject(DebugLog) Then Call CreateDebugLog("WriteLog")
				sVerbiage1 = "Found " & AppName & " v" & UninstKEY & " installed, which is older than the current package version of " & VERSION & "."
				sVerbiage2 = "Cylance should have auto upgraded, this will require manual remediation to upgrade."
				DebugLog.WriteLine(Now & "__ " & sVerbiage1) : DebugLog.WriteLine(Now & "__ " & sVerbiage2)
				sMsg = sVerbiage1 & "|" & sVerbiage2 & "||" & sMsg
				sAlreadyInst = "Upgrade"
			ElseIf COMP_VER = 1 Then
				If Not IsObject(DebugLog) Then Call CreateDebugLog("WriteLog")
				sVerbiage1 = "Found " & AppName & " v" & UninstKEY & " installed, which is newer than the version this script is coded to handle. Script requires updating."
				'DebugLog.WriteLine(Now & "__ Found " & AppName & " v" & UninstKEY & " installed, which is newer than the version this script is coded to handle. Update script! Quiting.")
				WSHLog "Warning", sVerbiage1
				sTicket = "True"
				sMsg = sVerbiage1 & "||" & sMsg
				Call Cleanup(sScript & "  Status", sMsg)
				WScript.Quit
			ElseIf COMP_VER = 0 Then
				sAlreadyInst = "Yes" ' Same version
			End If
		End If
	Else
		Call GetMSIInfo("Name", "Cylance", Null, "InString")
		If Not(IsNull(GUID) Or IsEmpty(GUID)) Then
			COMP_VER = CompareVersions(MSIver, VERSION)
			If COMP_VER = -1 Then
				If Not IsObject(DebugLog) Then Call CreateDebugLog("WriteLog")
				sVerbiage1 = "Found " & AppName & " v" & UninstKEY & " installed, which is older than the current package version of " & VERSION & "."
				sVerbiage2 = "Cylance should have auto upgraded, this will require manual remediation to upgrade."
				DebugLog.WriteLine(Now & "__ " & sVerbiage1) : DebugLog.WriteLine(Now & "__ " & sVerbiage2)
				sMsg = sVerbiage1 & "|" & sVerbiage2 & "||" & sMsg
				sAlreadyInst = "Upgrade"
			ElseIf COMP_VER = 1 Then
				If Not IsObject(DebugLog) Then Call CreateDebugLog("WriteLog")
				sVerbiage1 = "Found " & AppName & " v" & MSIver & " installed, which is newer than the version this script is coded to handle. Script requires updating."
				'DebugLog.WriteLine(Now & "__ Found " & AppName & " v" & UninstKEY & " installed, which is newer than the version this script is coded to handle. Update script! Quiting.")
				WSHLog "Warning", sVerbiage1
				sTicket = "True"
				sMsg = sVerbiage1 & "||" & sMsg
				Call Cleanup(sScript & "  Status", sMsg)
				WScript.Quit
			ElseIf COMP_VER = 0 Then
				sAlreadyInst = "Yes" ' Same version
			End If
		Else
			If Not IsObject(DebugLog) Then Call CreateDebugLog("WriteLog")
			DebugLog.WriteLine(Now & "__ No current or prior msi installations where found, going to check existence of service just in case.")
			Dim ChkSVC, sExePath, ExePathKEY
			ChkSVC = CheckService(sSVCName, "NULL", "NULL", "NoCheck", "NULL", "Required")
			If ChkSVC = "DoesntExist" Then
				DebugLog.WriteLine(Now & "__ Going to make one last check for well known areas of Cylance PROTECT x64 installation.")
				Call CheckforRemnants
			Else
				DebugLog.WriteLine(Now & "__ While no current or prior msi installations where found, the " & sSVCName & " service was found to exist. Remnants from a prior installation?")
				sExePath = "HKLM\System\CurrentControlSet\Services\" & sSVCName & "\ImagePath"
				ExePathKEY = GetRegData(sExePath, "{default}")
				If ExePathKEY = "{default}" Then
					DebugLog.WriteLine(Now & "__ Reg key " & sExePath & " does not exist. Dormant Service, remnants from a prior installation.")
					DebugLog.WriteLine(Now & "__ Going to check well known areas of Cylance PROTECT x64 installation.")
					Call CheckforRemnants
					sAlreadyInst = "Reinstall"			
				Else
					DebugLog.WriteLine(Now & "__ Found service registry key with path to executable to be " & sExePath & ". Remnants from a prior installation?")
					DebugLog.WriteLine(Now & "__ Going to check well known areas of Cylance PROTECT x64 installation.")
					Call CheckforRemnants
					sAlreadyInst = "Reinstall"
				End If
			End If
		End If
	End If
End Function

' Check if left over from prior install/corruption, etc.
Function CheckforRemnants
	On Error Resume Next
	Dim sPFN, sPD
	sPFN = "Cylance PROTECT x64"
	sPD = oProcessEnv("ProgramFiles") & "\Cylance"

	Dim sHKLMSoft, HKLMSoftKEY
	sHKLMSoft = "HKLM\SOFTWARE\Cylance\Desktop\Path"
	HKLMSoftKEY = GetRegData(sHKLMSoft, "{default}")
	If Not HKLMSoftKEY = "{default}" Then
		DebugLog.WriteLine(Now & "__ Found reg key " & sHKLMSoft & " exist. Remnants from a prior installation")
		sAlreadyInst = "Reinstall"
	End If

	If oFSO.FileExists(oProcessEnv("SYSTEMROOT") & "\" & sCacheName) Then
		DebugLog.WriteLine(Now & "__ File " & oProcessEnv("SYSTEMROOT") & "\" & sCacheName & " found to exist on file system. Remnants from a prior installation.")
		sAlreadyInst = "Reinstall"
	End If
	If oFSO.FileExists(sSystem32 & "drivers\" & sDrvName) Then
		DebugLog.WriteLine(Now & "__ File " & sSystem32 & "drivers\" & sDrvName & " found to exist on file system. Which is the Cylance Protect Driver. Remnants from a prior installation.")
		sAlreadyInst = "Reinstall"
	End If

	Dim sCheckPath1, sCheckPath2, f, fc
	sCheckPath1 = sPD
	If oFSO.FolderExists(sCheckPath1) Then
		Set f = oFSO.GetFolder(sCheckPath1)
		Set fc = f.Files
		If fc.Count > 0 Then
			DebugLog.WriteLine(Now & "__ Found " & fc.Count & " files exist in " & sCheckPath1 & ". Remnants from a prior installation.")
		Else
			DebugLog.WriteLine(Now & "__ Found " & sCheckPath1 & " directory exists. Remnants from a prior installation.")
		End If
		sAlreadyInst = "Reinstall"
		Set f = Nothing
		Set fc = Nothing
	End If

	sCheckPath2 = oProcessEnv("ProgramData") & "\Cylance\Desktop"
	If oFSO.FolderExists(sCheckPath2) Then
		Set f = oFSO.GetFolder(sCheckPath2)
		Set fc = f.Files
		If fc.Count > 0 Then
			DebugLog.WriteLine(Now & "__ Found " & fc.Count & " files exist in " & sCheckPath2 & ". Remnants from a prior installation.")
		Else
			DebugLog.WriteLine(Now & "__ Found " & sCheckPath2 & " directory exists. Remnants from a prior installation.")
		End If
		sAlreadyInst = "Reinstall"
		Set f = Nothing
		Set fc = Nothing
	End If

	If (IsNull(sAlreadyInst) Or IsEmpty(sAlreadyInst)) Then sAlreadyInst = "No"
	On Error GoTo 0
End Function

' Uninstall MSI function, cProp is for when you need to have a custom condition in MSIUnistall Function to do something custom :) otherwise it doesnt matter what is passed.
Function MSIUninstall(sDisplayOptions)
	Dim sScriptUninst, MSICMDLINE, sMsiLog, iUnstVal
	sScriptUninst = "Uninstall-" & AppName & "-v" & MSIver
	Call Stamp
	sMsiLog = Chr(34) & sScriptsDir & "\" & sScriptUninst & "-MSI-" & altDate & "-" & altTime & ".log" & Chr(34)
	MSICMDLINE = "msiexec.exe REBOOT=ReallySuppress /x " & chr(34) & GUID & chr(34) & " " & sDisplayOptions & " /L*v " & sMsiLog
	DebugLog.WriteLine(Now & "__ Issuing uninstall commandline: " & MSICMDLINE)
	If sMSIDebugQA = "False" Then
		iUnstVal = oShell.Run(MSICMDLINE, 1, True)
		iMSIVal = iUnstVal
		If iMSIVal = 0 Or iMSIVal = 3010 Then
			DebugLog.WriteLine(Now & "__ MSI uninstallation completed, result code: " & iMSIVal)
		Else
			DebugLog.WriteLine(Now & "__ MSI uninstallation FAILED, result code: " & iMSIVal)
			WSHLog "CriticalFailure", "MSI uninstallation FAILED, result code: " & iMSIVal
		End If
	ElseIf sMSIDebugQA = "True" Then
		Dim sDebugTest
		sDebugTest = "NOTEPAD.EXE"
		iUnstVal = oShell.Run(sDebugTest, 1, True)
	End If
End Function

' Function Get system uptime
Function GetUpTime(interval)
	On Error Resume Next
	Dim OpSysSet, OpSys, dtmBootup, dtmLastBootupTime, iErrDesc
	Set OpSysSet = oWMI.ExecQuery("SELECT LastBootUpTime FROM Win32_OperatingSystem WHERE Primary = True")
	If Err.Number = 0 Then
		For Each OpSys in OpSysSet
			dtmBootup = OpSys.LastBootUpTime 
			dtmLastBootupTime = WMIDateStringToDate(dtmBootup) 
			SystemUptimeMinutes = DateDiff(interval, dtmLastBootupTime, Now)
		Next 
	Else
		iErrCode = Err.Number
		iErrDesc = CStr(Err.Number) & " " & Err.Description & "."
		If Not IsObject(DebugLog) Then Call CreateDebugLog("WriteLog")
		DebugLog.WriteLine(Now & "__ Critical issue, could not connect to WMI, cimv2:Win32_OperatingSystem to obtain system uptime. Error Code: "  & iErrDesc)
		WSHLog "Failure", "Failed to connect to WMI, cimv2:Win32_OperatingSystem to obtain system uptime. Error Code: "  & iErrDesc
		SystemUptimeMinutes = "Failed"
	End If
	Set OpSysSet = Nothing
	On Error GoTo 0	
End Function


' ------   Wrapper 3.0.7 Functions/Subs section    -------

' Version comparison from string
Function Lsh(ByVal N, ByVal Bits)
  Lsh = N * (2 ^ Bits)
End Function

' Version comparison from string
Function GetVersionStringAsArray(ByVal Version)
  Dim VersionAll, VersionParts, N
  VersionAll = Array(0, 0, 0, 0)
  VersionParts = Split(Version, ".")
  For N = 0 To UBound(VersionParts)
    VersionAll(N) = CLng(VersionParts(N))
  Next

  Dim Hi, Lo
  Hi = Lsh(VersionAll(0), 16) + VersionAll(1)
  Lo = Lsh(VersionAll(2), 16) + VersionAll(3)

  GetVersionStringAsArray = Array(Hi, Lo)
End Function

' Version comparison from string
Function CompareVersions(ByVal Version1, ByVal Version2)
  Dim Ver1, Ver2, Result
  Ver1 = GetVersionStringAsArray(Version1)
  Ver2 = GetVersionStringAsArray(Version2)
  If Ver1(0) < Ver2(0) Then
    Result = -1
  ElseIf Ver1(0) = Ver2(0) Then
    If Ver1(1) < Ver2(1) Then
      Result = -1
    ElseIf Ver1(1) = Ver2(1) Then
      Result = 0
    Else
      Result = 1
    End If
  Else
    Result = 1
  End If
  CompareVersions = Result
End Function

' Using WindowsInstaller engine get MSI ProductCode status Function
Function GetMSIInfo(sCond, sProperty1, sProperty2, sMatch) ' Condition = 'Name' or 'GUID' we will be providing / The name or GUID itself ' If name the type of match
	Call ResetMSIvalues
	On Error Resume Next
	Dim product, products, GetAppName
	Set oMSIinstaller = CreateObject("WindowsInstaller.Installer") : CheckError
	If oMSIinstaller Is Nothing Then
		iErrCode = "429"
		WSHLog "CriticalFailure", "Could not connect to WindowsInstaller.Installer" & Chr(13) & "Windows Installer Engine." & sMsg
	Else
		Set products = oMSIinstaller.Products : CheckError
		For Each product In products
			If UCASE(sCond) = "NAME" Then
				GetAppName = oMSIinstaller.ProductInfo(product, "ProductName")
				If UCASE(sMatch) = "EXACT" Then
					If UCASE(GetAppName) = UCASE(sProperty1) Then
						AppName = GetAppName
						GUID = product
						MSIver = oMSIinstaller.ProductInfo(product, "VersionString") : CheckError ' Installed
					End If
				Else
					If Not UCASE(sMatch) = "INSTR" Then
						If InStr(UCASE(GetAppName), UCASE(sProperty1)) And Not InStr(UCASE(GetAppName), UCASE(sMatch)) >0 Then
							AppName = GetAppName
							GUID = product
							MSIver = oMSIinstaller.ProductInfo(product, "VersionString") : CheckError ' Installed
						End If
					Else
						If InStr(UCASE(GetAppName), UCASE(sProperty1)) Then
							If InStr(UCASE(GetAppName), UCASE(sProperty2)) Then
								AppName = GetAppName
								GUID = product
								MSIver = oMSIinstaller.ProductInfo(product, "VersionString") : CheckError ' Installed
							End If
						End If
					End If
				End If
			ElseIf UCASE(sCond) = "GUID" Then
				If UCASE(product) = UCASE(sProperty1) Then
					AppName = oMSIinstaller.ProductInfo(product, "ProductName")
					GUID = product
					MSIver = oMSIinstaller.ProductInfo(product, "VersionString") : CheckError ' Installed
				End If
			End If
		Next
	End If
	Set oMSIinstaller = Nothing
	On Error GoTo 0
End Function

' Checks MSI Installer object errors Sub
Sub CheckError
	Dim message, errRec
	If Err = 0 Then Exit Sub
	message = Err.Source & " " & Hex(Err) & ": " & Err.Description
	If Not oMSIinstaller Is Nothing Then
		Set errRec = oMSIinstaller.LastErrorRecord
		If Not errRec Is Nothing Then message = message & ": " & errRec.FormatText
	End If
	iErrCode = Err.Number
	WSHLog "CriticalFailure", message  & ". " & sMsg
End Sub

' Reset MSI Values Sub
Sub ResetMSIvalues
	AppName = Null
	GUID = Null
	MSIver = Null
End Sub

' Check state of process(es), or Kill Process(es) Function
Function CheckProcess(pCond, pName, pMatch, action, sleeptime) ' Begins, Like, or Exact / %ProcessName / commandline match / Kill or Check
	On Error Resume Next
	Dim Process, oProcess, iCount
	If pCond = "Begins" Then
		Set Process = oWMI.ExecQuery("SELECT Name FROM Win32_Process WHERE Name LIKE '" & pName & "%'")
	ElseIf pCond = "Like" Then
		Set Process = oWMI.ExecQuery("SELECT Name FROM Win32_Process WHERE Name LIKE '%" & pName & "%'")
	ElseIf pCond = "Exact" Then
		Set Process = oWMI.ExecQuery("SELECT Name FROM Win32_Process WHERE Name ='" & pName & "'")
	End If
	If Process.Count = 0 Then
		CheckProcess = "notrunning" '"stopped"
	Else
		CheckProcess = "started"
		iCount = 1
		If action = "Kill" Then
			For Each oProcess In Process
				If Not IsNull(pMatch) Then
					If InStr(UCase(oProcess.CommandLine), UCase(pMatch)) Then
						oProcess.Terminate()
						If iCount < 2 Then
							DebugLog.WriteLine(Now & "__ Terminated: " & oProcess.Name & " process, ID: " & oProcess.ProcessId & " , CMDLine: " & oProcess.CommandLine)
						ElseIf iCount => 2 Then
							DebugLog.WriteLine(Now & "__ Terminated: " & oProcess.Name & " process, #" & iCount & ", ID: " & oProcess.ProcessId & " , CMDLine: " & oProcess.CommandLine)
						End If
					End If
				Else
					oProcess.Terminate()
					If iCount < 2 Then
						DebugLog.WriteLine(Now & "__ Terminated: " & oProcess.Name & " process, ID: " & oProcess.ProcessId & " , CMDLine: " & oProcess.CommandLine)
					ElseIf iCount => 2 Then
						DebugLog.WriteLine(Now & "__ Terminated: " & oProcess.Name & " process, #" & iCount & ", ID: " & oProcess.ProcessId & " , CMDLine: " & oProcess.CommandLine)
					End If
					iCount = iCount + 1
				End If
			Next
			WScript.Sleep sleeptime ' sleep to release files to be deleted. 5000 equals 5 seconds.
		Else
			' Process Audit mode
			For Each oProcess In Process
				If iCount < 2 Then
					DebugLog.WriteLine(Now & "__ Process found running: " & oProcess.Name & " process, ID: " & oProcess.ProcessId & " , CMDLine: " & oProcess.CommandLine)
				ElseIf iCount => 2 Then
					DebugLog.WriteLine(Now & "__ Process found running: " & oProcess.Name & " process, #" & iCount & ", ID: " & oProcess.ProcessId & " , CMDLine: " & oProcess.CommandLine)
				End If
			Next                                     
		End If
	End If
	On Error GoTo 0
End Function

' Check state of Service and manipulate it Function
Function CheckService(Name, Startup, Startup2, Status, DelayedAutoStart, Required)
	On Error Resume Next
	Dim oWin32Service, iErrDesc
	Set oWin32Service = GetObject("winmgmts:\\.\root\cimv2:Win32_Service")
	If Err.Number <> 0 Then
		iErrCode = Err.Number
		iErrDesc = CStr(Err.Number) & " " & Err.Description & "."
		If Not IsObject(DebugLog) Then Call CreateDebugLog("WriteLog")
		DebugLog.WriteLine(Now & "__ Unable to connect to WMI namespace \root\cimv2:Win32_Service. ")
		WSHLog "CriticalFailure", "Unable to connect to WMI namespace \root\cimv2:Win32_Service. Error code: " & iErrDesc
	End If
	Dim oSvc, sStartMode, sState, iMode, sStartMode2, iState, sState2, sExitCode, sSVCKey, SVCKey
	Dim sleepDur, SleepTime, iSleepDur
	SleepTime = "1000"
	iSleepDur = "3"
	Set oSvc = Getobject("winmgmts:root\cimv2:Win32_Service.Name='" & Name & "'")
	If Err.Number <> 0 Then
		If UCASE(Required) = "REQUIRED" Then
			If Not IsObject(DebugLog) Then Call CreateDebugLog("WriteLog")
			DebugLog.WriteLine(Now & "__ Service: " & Name & ", does not exist on this system and is a mandatory service.")
			'WSHLog "CriticalFailure", "Service: " & Name & ", does not exist on this system and is a mandatory service."
			CheckService = "DoesntExist"
			Exit Function
		Else
			If Not IsObject(DebugLog) Then Call CreateDebugLog("WriteLog")
			DebugLog.WriteLine(Now & "__ Service: " & Name & ", does not exist on this system")
			Exit Function
		End If
	Else
		sStartMode = oSvc.StartMode
		sState = oSvc.State
		If Not UCase(Startup) = "NULL" Then
			If sStartMode <> Startup Then
				iMode = oSvc.ChangeStartMode(Startup2)
				sExitCode = ResolveSVCcode(iMode)
				Set oSvc = Getobject("winmgmts:root\cimv2:Win32_Service.Name='" & Name & "'")
				sStartMode2 = oSvc.StartMode
				sState = oSvc.State
				If sStartMode2 <> Startup Then
					If Not IsObject(DebugLog) Then Call CreateDebugLog("WriteLog")
					DebugLog.WriteLine(Now & "__ Failed to change Service: " & Name & ", from " & sStartMode & " to " & Startup2 & " (" & sExitCode & ")")
					WSHLog "Warning", "Failed to change Service: " & Name & ", from " & sStartMode & " to " & Startup2 & " (" & sExitCode & ")"
				Else
					If Not IsObject(DebugLog) Then Call CreateDebugLog("WriteLog")
					DebugLog.WriteLine(Now & "__ Service: " & Name & ", was successfully changed from " & sStartMode & " to " & Startup2)
				End If
			Else
				If IsObject(DebugLog) Then DebugLog.WriteLine(Now & "__ Service: " & Name & ", found startup type already set to: " & sStartMode)
			End If
		End If
		sleepDur = 0
		If Not Status = "NoCheck" Then
			If Status = "Stopped" Then
				If sState <> Status Then                                                               
					iState = oSvc.StopService()
					sExitCode = ResolveSVCcode(iState)
					Do Until sState2 = "Stopped" Or sState2 = "Exceeded Wait Time"
						Wscript.Sleep SleepTime
						Set oSvc = Getobject("winmgmts:root\cimv2:Win32_Service.Name='" & Name & "'")
						sState2 = oSvc.State
						sleepDur = sleepDur + 1
						If sleepDur = iSleepDur Then
							sState2 = "Exceeded Wait Time" ' to get out of loop
						End If
					Loop
					If sleepDur = iSleepDur Then
						If Not IsObject(DebugLog) Then Call CreateDebugLog("WriteLog")
						DebugLog.WriteLine(Now & "__ Service: " & Name & ", was found " & sState & "; attempt to stop the service failed OR Wait Time expired! (" & sExitCode & ")")
						WSHLog "Warning", "Service: " & Name & ", was found " & sState & "; attempt to stop the service failed OR Wait Time expired! (" & sExitCode & ")"
					Else
						If Not IsObject(DebugLog) Then Call CreateDebugLog("WriteLog")
						DebugLog.WriteLine(Now & "__ Service: " & Name & ", was found " & sState & "; attempt to stop the service Succeeded.")
					End If
				End If
			ElseIf Status = "Running" Then
				If sState <> Status Then
					iState = oSvc.StartService()
					sExitCode = ResolveSVCcode(iState)
					Do Until sState2 = "Running" Or sState2 = "Exceeded Wait Time"
						WScript.Sleep SleepTime
						Set oSvc = Getobject("winmgmts:root\cimv2:Win32_Service.Name='" & Name & "'")
						sState2 = oSvc.State
						sleepDur = sleepDur + 1
						If sleepDur = iSleepDur Then
							sState2 = "Exceeded Wait Time" ' to get out of loop
						End If
					Loop
					If sleepDur = iSleepDur Then
						If Not IsObject(DebugLog) Then Call CreateDebugLog("WriteLog")
						DebugLog.WriteLine(Now & "__ Service: " & Name & ", was found " & sState & "; attempt to start the service failed OR Wait Time expired! (" & sExitCode & ")")
						CheckService = "FailedtostartService"
						WSHLog "Warning", "Service: " & Name & ", was found " & sState & "; attempt to start the service failed OR Wait Time expired! (" & sExitCode & ")"
					Else
						If Not IsObject(DebugLog) Then Call CreateDebugLog("WriteLog")
						DebugLog.WriteLine(Now & "__ Service: " & Name & ", was found " & sState & "; attempt to start the service Succeeded.")
						CheckService = "ServiceStarted"
					End If
				End If
			End If
		End If
		If DelayedAutoStart = "Delayed" Then
			sSVCKey = "HKLM\SYSTEM\CurrentControlSet\Services\" & Name & "\DelayedAutoStart"
			SVCKey = GetRegData(sSVCKey, "{default}")
			If SVCKey = "{default}" Then
				oShell.RegWrite sSVCKey, "1", "REG_DWORD"
				If Err.Number <> 0 Then
					If Not IsObject(DebugLog) Then Call CreateDebugLog("WriteLog")
					DebugLog.WriteLine(Now & "__ Found " & sSVCKey & " missing, could not create reg Key with a value of 1 (Delayed Start).")
					WSHLog "Warning", "Found " & sSVCKey & " missing, could not create reg Key with a value of 1 (Delayed Start)."
				Else
					If Not IsObject(DebugLog) Then Call CreateDebugLog("WriteLog")
					DebugLog.WriteLine(Now & "__ Successfully created " & sSVCKey & ", with value of 1 (Delayed Start).")
				End If
			ElseIf Not SVCKey = "1" Then
				oShell.RegWrite sSVCKey, "1", "REG_DWORD"
				If Err.Number <> 0 Then
					iErrCode = Err.Number
					iErrDesc = CStr(Err.Number) & " " & Err.Description & "."
					If Not IsObject(DebugLog) Then Call CreateDebugLog("WriteLog")
					DebugLog.WriteLine(Now & "__ Found " & sSVCKey & " set to " & SVCKey & ", was not able to change it to a value of 1 (Delayed Start). Error code: " & iErrDesc)
					WSHLog "Warning", "Found " & sSVCKey & " set to " & SVCKey & ", was not able to change it to a value of 1 (Delayed Start). Error code: " & iErrDesc
				Else
					If Not IsObject(DebugLog) Then Call CreateDebugLog("WriteLog")
					DebugLog.WriteLine(Now & "__ Successfully changed " & sSVCKey & ", with value of 1 (Delayed Start).")
				End If
			End If
		End If
	End If
	On Error GoTo 0
End Function

' Resolve Exit Code to WMI Service Change
Function ResolveSVCcode(svccode)
	Select Case svccode
		Case 0 : ResolveSVCcode = "Success"
		Case 1 : ResolveSVCcode = "Not Supported"
		Case 2 : ResolveSVCcode = "Access Denied"
		Case 3 : ResolveSVCcode = "Dependent Services Running"
		Case 4 : ResolveSVCcode = "Invalid Service Control"
		Case 5 : ResolveSVCcode = "Service Cannot Accept Control"
		Case 6 : ResolveSVCcode = "Service Not Active"
		Case 7 : ResolveSVCcode = "Service Request Timeout"
		Case 8 : ResolveSVCcode = "Unknown Failure"
		Case 9 : ResolveSVCcode = "Path Not Found"
		Case 10 : ResolveSVCcode = "Service Already Running"
		Case 11 : ResolveSVCcode = "Service Database Locked"
		Case 12 : ResolveSVCcode = "Service Dependency Deleted"
		Case 13 : ResolveSVCcode = "Service Dependency Failure"
		Case 14 : ResolveSVCcode = "Service Disabled"
		Case 15 : ResolveSVCcode = "Service Logon Failed"
		Case 16 : ResolveSVCcode = "Service Marked For Deletion"
		Case 17 : ResolveSVCcode = "Service No Thread"
		Case 18 : ResolveSVCcode = "Status Circular Dependency"
		Case 19 : ResolveSVCcode = "Status Duplicate Name"
		Case 20 : ResolveSVCcode = "Status Invalid Name"
		Case 21 : ResolveSVCcode = "Status Invalid Parameter"
		Case 22 : ResolveSVCcode = "Status Invalid Service Account"
		Case 23 : ResolveSVCcode = "Status Service Exists"
		Case 24 : ResolveSVCcode = "Service Already Paused"
		Case Else : ResolveSVCcode = svccode
	End Select
End Function

' Read REG_BINARY value and return it as a text string
Function GetRegBinary(sRegValue, vDefault)
	On Error Resume Next
	Dim aBin, aInt(), i, iBinSize, sString, iChar, sChar
	aBin = oShell.RegRead(sRegValue)
	If Err.Number = 0 Then
		iBinSize = UBound(aBin)
		ReDim aInt(iBinSize)
		For i = LBound(aBin) To UBound(aBin)
		aInt(i) = CInt(aBin(i))
		If aInt(i) <> 0 Then
		iChar = aInt(i)
		sChar = Chr(iChar)
		sString = sString & sChar
		End If
		Next
		GetRegBinary = sString
	Else
		GetRegBinary = vDefault
	End If
	On Error GoTo 0
End Function

' Will delete any regkey and it subkeys Sub
Sub DeleteRegKeys(HIVE, sHive, sKeyPath)
	On Error Resume Next
	Const HKEY_CLASSES_ROOT = &H80000000: Const HKEY_CURRENT_USER = &H80000001
	Const HKEY_LOCAL_MACHINE = &H80000002: Const HKEY_USERS = &H80000003
	Const HKEY_CURRENT_CONFIG = &H80000005: Const HKEY_DYN_DATA = &H80000006
	Dim oReg, aSubkeys, sSubkey, iReturn
	Select Case sHive
		Case "HKCR": sHive = &H80000000
		Case "HKCU": sHive = &H80000001
		Case "HKLM": sHive = &H80000002
		Case "HKU": sHive = &H80000003
		Case "HKCC": sHive = &H80000004
		Case "HKDD": sHive = &H80000005
	End Select
	If sHive = "" Then sHive = HKEY_LOCAL_MACHINE
	Set oReg = GetObject("winmgmts:\\.\root\default:StdRegProv")
	oReg.EnumKey sHive, sKeyPath, aSubkeys
	If IsArray(aSubkeys) Then
		For Each sSubkey In aSubkeys
			DeleteRegKeys HIVE, sHive, sKeyPath & "\" & sSubkey
		Next
	End If
	iReturn = oReg.DeleteKey(sHive, sKeyPath)
	If iReturn = 0 Then
		DebugLog.WriteLine(Now & "__ Succeeded deleting: " & HIVE & "\" & sKeyPath)
	Else
		DebugLog.WriteLine(Now & "__ Failed deleting: " & HIVE & "\" & sKeyPath)
		WSHLog "Warning", "Failed deleting: " & HIVE & "\" & sKeyPath
	End If
	On Error GoTo 0
End Sub

Sub RegKeyCheck(sRegKey, Hive, HiveDupe)
	Dim sHive
	Select Case Hive
		Case "HKCR": sHive = "HKEY_CLASSES_ROOT"
		Case "HKCU": sHive = "HKEY_CURRENT_USER"
		Case "HKLM": sHive = "HKEY_LOCAL_MACHINE"
		Case "HKU": sHive = "HKEY_USERS"
		Case "HKCC": sHive = "HKEY_CURRENT_CONFIG"
		Case "HKDD": sHive = "HKEY_DYN_DATA"
	End Select
	On Error Resume Next
	Dim sCheck
	sCheck = oShell.RegRead(sHive & "\" & sRegKey & "\")
	If Err.Number = 0 Then
		DebugLog.WriteLine(Now & "__ Found " & sHive & "\" & sRegKey)
		Call DeleteRegKeys(Hive, HiveDupe, sRegKey)
	End If
	On Error GoTo 0
End Sub

' Gets Manufacturer/Model info on the device
Function GetDeviceInfo
	On Error Resume Next
	If sWMIFailed = "Failed" Then
		sMsg = sMsg & "|Manufacturer: (WMI not available)  Model: (WMI not available)." ' Do not remove this last & "." otherwise it wraps
	Else
		Dim DeviceInfo, oField, count, sManuf, CheckNull, sModel
		Set DeviceInfo = oWMI.ExecQuery("SELECT Manufacturer, Model FROM Win32_ComputerSystem")
		If Err.Number = 0 Then
			For Each oField in DeviceInfo
			    sManuf = oField.Manufacturer
			    CheckNull = Right(sManuf, 1)
			    Do
					sManuf = Left(sManuf, Len(sManuf)- 1)
				    count = Len(sManuf)
					CheckNull = Right(sManuf, 1)
					If CheckNull <> " " Then CheckNull = True
			    Loop Until CheckNull = True
			    sManuf = Left(oField.Manufacturer, Len(sManuf)+1)
			    CheckNull = False
			    sModel = oField.Model
			    CheckNull = Right(sModel, 1)
			    Do
					sModel = Left(sModel, Len(sModel)- 1)
				    count = Len(sModel)
					CheckNull = Right(sModel, 1)
					If CheckNull <> " " Then CheckNull = True
			    Loop Until CheckNull = True
			    sModel = Left(oField.Model, Len(sModel)+1)
			Next
'		Else
'			WSHLog "Failure", "Critical issue, could not connect to WMI, cimv2:Win32_ComputerSystem to obtain Manufacturer/Model information."
		End If
		sMsg = sMsg & "|Manufacturer: " & sManuf & "  Model: " & sModel & "." ' Do not remove this last & "." otherwise it wraps
		Set DeviceInfo = Nothing
	End If
	On Error GoTo 0
End Function

' Get Network Interface
Function GetNetworkInterface
	On Error Resume Next
	If sWMIFailed = "Failed" Then
		sMsg = sMsg & "|Ethernet Adapter: (WMI not available) |IP address: (WMI not available) |Subnet Mask: (WMI not available)"
	Else
		Dim Adapters, Adapter, i, period, prevperiod, addressbyte, addressbytes(4)
		Dim subnetmaskbyte, subnetmaskbytes(4)
		Set Adapters = oWMI.ExecQuery("SELECT DefaultIPGateway, Description, IPAddress, IPSubnet FROM Win32_NetworkAdapterConfiguration WHERE IPEnabled=TRUE")
		If Err.Number = 0 Then
			For Each Adapter In Adapters
				If Not IsNull(Adapter.DefaultIPGateway) Then
					If Not IsNull(Adapter.IPAddress) Then
						description = Adapter.Description
						address = Adapter.IPAddress(0)
						subnet = Adapter.IPSubnet(0)
						subnetmask = Adapter.IPSubnet(0)
						i = 0
						period = 1
						While period <> Len(address) + 2
							prevperiod = period
							period = InStr(period + 1, address, ".") + 1
							If period = 1 Then period = Len(address) + 2
							addressbyte = Mid(address, prevperiod, period-prevperiod - 1)
							addressbytes(i) = addressbyte
							i = i + 1
						Wend
						i = 0
						period = 1
						While period <> Len(subnet) + 2
							prevperiod = period
							period = Instr(period + 1, subnet, ".") + 1
							If period = 1 Then period = Len(subnet) + 2
							subnetmaskbyte = Mid(subnet, prevperiod, period-prevperiod - 1)
							subnetmaskbytes(i) = subnetmaskbyte
							i = i + 1
						Wend
						subnet = ""
						For i = 0 to 3
							subnet = subnet & (addressbytes(i) AND subnetmaskbytes(i)) & "."
						Next
						subnet = Left(subnet, Len(subnet)-1)
					End If
				End If
			Next
'		Else
'			WSHLog "Failure", "Critical issue, could not connect to WMI, cimv2:Win32_NetworkAdapterConfiguration to obtain Network Adapter Configuration."
		End If
		sMsg = sMsg & "|Ethernet Adapter: " & description & ".|IP address: " & address & "|Subnet Mask: " & subnetmask ' do not remove the dot in ".|IP address" otherwise it wraps
		Set Adapters = Nothing
	End If
	On Error GoTo 0
End Function

' Get Time Zone description
Function GetTZ
	On Error Resume Next
	Dim sTZ, sSTN
	If InStr(UCase(ProductName), "XP") Or InStr(UCase(ProductName), "2000") Or InStr(UCase(ProductName), "2003") Then
		sTZ = "HKLM\SYSTEM\CurrentControlSet\Control\TimeZoneInformation\StandardName"
'	ElseIf InStr(UCase(ProductName), "VISTA") Or InStr(UCase(ProductName), "7") Or InStr(UCase(ProductName), "2008") Then
'		sTZ = "HKLM\SYSTEM\CurrentControlSet\Control\TimeZoneInformation\TimeZoneKeyName"
	Else
'		TZ = "TZ function does not account for this OS"
		sTZ = "HKLM\SYSTEM\CurrentControlSet\Control\TimeZoneInformation\TimeZoneKeyName"
	End If
	If InStr(sTZ, "HKLM") Then
		sSTN = GetRegData(sTZ, "{default}")
		If sSTN = "{default}" Then
			TZ = "Could not read from registry."
		ElseIf sSTN = "" Then
			TZ = "No value found in registry."
		Else
			TZ = sSTN
		End If
	End If
	sMsg = sMsg & "|Time Zone: " & TZ & "." ' Do not remove this last & "." otherwise it wraps (do too some times zones ex. Malay Peninsula Standard Time)
	On Error GoTo 0
End Function

' Get OS Language we are dealing With
Function GetOSLocale
	On Error Resume Next
	If sWMIFailed = "Failed" Then
		sMsg = sMsg & "|System boot time: (WMI not available) "
		sMsg = sMsg & "|Locale: (WMI not available) "
	Else
		Dim OpSysSet, OpSys, dtmBootup, dtmLastBootupTime, dtmSystemUptime, sKeyPath, CTL, LocaleID
		Set OpSysSet = oWMI.ExecQuery("SELECT LastBootUpTime, OSLanguage FROM Win32_OperatingSystem WHERE Primary = True")
		If Err.Number = 0 Then
			For Each OpSys in OpSysSet
				dtmBootup = OpSys.LastBootUpTime 
				dtmLastBootupTime = WMIDateStringToDate(dtmBootup) 
				dtmSystemUptime = DateDiff("d", dtmLastBootupTime, Now)
				LBT = dtmLastBootupTime & " (" & dtmSystemUptime & " days)"
				iOSLang = OpSys.OSLanguage
			    sOSLangHex = Right("000" & Hex(iOSLang), 4)
			Next 
			sKeyPath = "HKLM\SOFTWARE\Classes\MIME\Database\Rfc1766\" & sOSLangHex
			sOSLanguage = GetRegData(sKeyPath, "{default}")
			CTL = Split(sOSLanguage, ";")'Vista and up
			If Left(CTL(1), 1) = "@" Then
				LocaleID = TranslateOSLocale(iOSLang)
				sOSLanguage = CTL(0) & ";" & LocaleID
			End If
		Else
'			WSHLog "Failure", "Critical issue, could not connect to WMI, cimv2:Win32_OperatingSystem to obtain OS Language."
		End If
		sMsg = sMsg & "|System boot time: " & LBT & ". |Locale: " & iOSLang & ", " & sOSLanguage & "." ' Do not remove this last & "." otherwise it wraps
		Set OpSysSet = Nothing
	End If
	On Error GoTo 0
End Function

' Function to convert UNC time to readable format 
Function WMIDateStringToDate(dtmBootup) 
	WMIDateStringToDate = CDate(Mid(dtmBootup, 5, 2) & "/" & Mid(dtmBootup, 7, 2) & "/" & Left(dtmBootup, 4) _ 
	& " " & Mid (dtmBootup, 9, 2) & ":" & Mid(dtmBootup, 11, 2) & ":" & Mid(dtmBootup, 13, 2))
End Function 

' Converts Locale ID to friendly name
Function TranslateOSLocale(LocaleID)
	Select Case LocaleID
		Case ("1025") : TranslateOSLocale = "Arabic"
		Case ("1046") : TranslateOSLocale = "Brazilian"
		Case ("2052") : TranslateOSLocale = "Chinese (Simplified)"
		Case ("1028") : TranslateOSLocale = "Chinese (Traditional)"
		Case ("3076") : TranslateOSLocale = "Chinese (Hong Kong)"
		Case ("1029") : TranslateOSLocale = "Czech"
		Case ("1030") : TranslateOSLocale = "Danish"
		Case ("1043") : TranslateOSLocale = "Dutch"
		Case ("1033") : TranslateOSLocale = "English"
		Case ("1035") : TranslateOSLocale = "Finnish"
		Case ("1036") : TranslateOSLocale = "French"
		Case ("1031") : TranslateOSLocale = "German"
		Case ("1032") : TranslateOSLocale = "Greek"
		Case ("1037") : TranslateOSLocale = "Hebrew"
		Case ("1038") : TranslateOSLocale = "Hungarian"
		Case ("1040") : TranslateOSLocale = "Italian"
		Case ("1041") : TranslateOSLocale = "Japanese"
		Case ("1042") : TranslateOSLocale = "Korean"
		Case ("1044") : TranslateOSLocale = "Norwegian"
		Case ("1045") : TranslateOSLocale = "Polish"
		Case ("2070") : TranslateOSLocale = "Portuguese"
		Case ("1049") : TranslateOSLocale = "Russian"
		Case ("3082") : TranslateOSLocale = "Spanish"
		Case ("1053") : TranslateOSLocale = "Swedish"
		Case ("1055") : TranslateOSLocale = "Turkish"
		Case Else : TranslateOSLocale = sOSLanguage
	End Select
End Function

' Get OS Install Date
Function GetInstallDate
	On Error Resume Next
	Dim sNT, sInstallDate, InstallDate
	sNT = "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\InstallDate"
	sInstallDate = GetRegData(sNT, "{default}")
	If sInstallDate = "{default}" Then
		InstallDate = "Could not read from registry."
	ElseIf sInstallDate = "" Then
		InstallDate = "No value found in registry."
	Else
		InstallDate = DateAdd("s", sInstallDate, DateSerial(1970, 1, 1))
		InstallDate = DateAdd("h", -4, InstallDate)
	End If	
	sMsg = sMsg & "|OS Install Date: " & InstallDate & " (EST). "
	On Error GoTo 0
End Function

' Get what OS, CSD version are we working with
Function GetOSInfo
	On Error Resume Next
	Dim sPN, sId, sCSDVer
	sPN = "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProductName"
	ProductName = GetRegData(sPN, "{default}")
	If ProductName = "{default}" Then
		iErrCode = "53"
		DebugLog.WriteLine(Now & "__ GetOSInfo function failed to read reg Key: " & sPN)
		WSHLog "CriticalFailure", "GetOSInfo function failed to read reg Key: " & sPN
	ElseIf ProductName = "" Then
		iErrCode = "53"
		DebugLog.WriteLine(Now & "__ GetOSInfo- function found a Null value in reg Value: " & sPN)
		WSHLog "CriticalFailure", "GetOSInfo- function found a Null value in reg Value: " & sPN
	End If
	If InStr(UCase(ProductName), "SERVER") Then
		IsServer = "True"	
		If Not oFSO.FileExists(oProcessEnv("SYSTEMROOT") & "\explorer.exe") Then
			IsServerCore = "True"
		End If
	ElseIf InStr(UCase(ProductName), "WINDOWS 10") Then
		sId = "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ReleaseId"
		CBVer = GetRegData(sId, "{default}")
		If CBVer = "{default}" Then
			iErrCode = "53"
			DebugLog.WriteLine(Now & "__ GetOSInfo function on Windows 10 failed to read reg Key: " & sId)
			WSHLog "CriticalFailure", "GetOSInfo function on Windows 10 failed to read reg Key: " & sId
		ElseIf ProductName = "" Then
			iErrCode = "53"
			DebugLog.WriteLine(Now & "__ GetOSInfo- function on Windows 10 found a Null value in reg Value: " & sId)
			WSHLog "CriticalFailure", "GetOSInfo- function on Windows 10 found a Null value in reg Value: " & sId
		End If
	End If
	sCSDVer = "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\CSDVersion"
	CSDver = GetRegData(sCSDVer, "{default}")
	If CSDver = "{default}" Then CSDver = "RTM"
	s64Bit = "0"
	If oFSO.FileExists(oProcessEnv("SYSTEMROOT") & "\SysWOW64\wscript.exe") Then
		s64Bit = "1"
	End If
	' Check if corp build
	sPBV = sEngKey & "Build\BuildVersion"
	sPTV = sEngKey & "Build\DeployTSVersion"
	PBV = GetRegData(sPBV, "{default}")
	PTV = GetRegData(sPTV, "{default}")
	If PBV = "{default}" OR PBV = "" Or IsEmpty(PBV) Or IsNull(PBV) Then PBV = "Non-Corp"
	If PTV = "{default}" Or PTV = "" Or IsEmpty(PTV) Or IsNull(PTV) Then PTV = "No value"
	If InStr(UCase(ProductName), "10") Then
		sMsg = sMsg & "|Hostname: " & sCN & "|OS: " & ProductName & ", CB: " & CBVer & "|BuildVer: " & PBV & "|DeployTSVer: " & PTV & "." ' Do not remove this last & "." otherwise it wraps
	Else
		sMsg = sMsg & "|Hostname: " & sCN & "|OS: " & ProductName & ", " & CSDver & "|BuildVer: " & PBV & "|DeployTSVer: " & PTV & "." ' Do not remove this last & "." otherwise it wraps
	End If
	On Error GoTo 0
End Function

' Check if DC Function 
Function IsDC
	On Error Resume Next
	Dim DeviceInfo, oDomain
	Set DeviceInfo = oWMI.ExecQuery("SELECT DomainRole FROM Win32_ComputerSystem")
	If Err.Number = 0 Then
		For Each oDomain In DeviceInfo 
			If oDomain.DomainRole = 4 Or oDomain.DomainRole = 5 Then DC = "True"
		Next
	Else
		WSHLog "CriticalFailure", "Critical issue, could not connect to WMI, cimv2:Win32_ComputerSystem to obtain DomainRole information."
	End If
	Set DeviceInfo = Nothing
	On Error GoTo 0
End Function

' Read Registry Key/Values Function
Function GetRegData(sRegKey, vDefault)
	On Error Resume Next
	Dim vReturn
	vReturn = oShell.RegRead(sRegKey)
	If Err.Number <> 0 Then
		GetRegData = vDefault
		Err.Clear
	Else
		GetRegData = vReturn
	End If
	On Error GoTo 0
End Function

' Get ID of who is running the script, as well as who is logged on (if anyone) Function
Function GetWhoIsRunningScript
	On Error Resume Next
	If sWMIFailed = "Failed" Then
		sMsg = sMsg & "|Script was executed utilizing account: (WMI not available) "
		sMsg = sMsg & "|Following account(s) are logged on during execution of script: (WMI not available) "
	Else
		Dim cProcessList, oProcess, cProperties, sNameOfUser, sUserDomain
		Set cProcessList = oWMI.ExecQuery("SELECT CommandLine,Name FROM Win32_Process WHERE " & "Name = 'cscript.exe' or Name = 'wscript.exe'")
		For Each oProcess in cProcessList
			If InStr(oProcess.CommandLine, Wscript.ScriptFullName) Then
				cProperties = oProcess.GetOwner(sNameOfUser,sUserDomain)
			End If
		Next
		If IsObject(DebugLog) Then DebugLog.WriteLine(Now & "__ Script was executed utilizing account: " & sUserDomain & "\" & sNameOfUser)
		sMsg = sMsg & "|Script was executed utilizing account: " & sUserDomain & "\" & sNameOfUser & "."	
	
		Dim cCS, oCS, LoggedInUser
		Set cCS = oWMI.ExecQuery("SELECT UserName FROM Win32_ComputerSystem")
		LoggedInUser = "NOONE"
		For Each oCS in cCS
			LoggedInUser = oCS.UserName
		Next
		If IsNull(LoggedInUser) Then
			If IsObject(DebugLog) Then DebugLog.WriteLine(Now & "__ Script detected the following account(s) are logged on during start of installation: No Logon detected")
			sMsg = sMsg & "|Following account(s) are logged on during execution of script: No Logon detected"
		Else
			If IsObject(DebugLog) Then DebugLog.WriteLine(Now & "__ Script detected the following account(s) are logged on during start of installation: " & LoggedInUser)
			sMsg = sMsg & "|Following account(s) are logged on during execution of script: " & LoggedInUser
		End If
	End If
	On Error GoTo 0
End Function

' Creates Corporate Engineering debug log (or append to existing log if run before) Function
Function CreateDebugLog(sCondition)
	If sCondition = "WriteLog" Then
		Call CreateDebugLogNow
	Else
		If (sCoreBlat <> "" Or sCoreBlat2 <> "" Or sCoreWMI <> "" Or sCoreInfo <> "" Or sCoreInfo2 <> "" Or sCoreInfo3 <> "") Then
			Call CreateDebugLogNow
		End If
	End If
End Function

Sub CreateDebugLogNow
	On Error Resume Next
	Const ForReading = 1 : Const ForWriting = 2 : Const ForAppending = 8
	Dim iErrDesc
	If NOT oFSO.FileExists(sDebugLog) Then
		Set DebugLog = oFSO.OpenTextFile(sDebugLog, ForWriting, True)
		If Err.Number <> 0 Then
			iErrCode = Err.Number
			iErrDesc = CStr(Err.Number) & " " & Err.Description & "."
			Call GetWhoIsRunningScript
			WSHLog "CriticalFailure", "Attempt to create Debuglog: " &  sDebugLog & ", failed. Error code: " & iErrDesc
		End If
	Else ' Append to existing log
		Set DebugLog = oFSO.OpenTextFile(sDebugLog, ForAppending, True)
		If Err.Number <> 0 Then
			iErrCode = Err.Number
			iErrDesc = CStr(Err.Number) & " " & Err.Description & "."
			Call GetWhoIsRunningScript
			WSHLog "CriticalFailure", "Attempt to append to Debuglog: " &  sDebugLog & ", failed. Error code: " & iErrDesc
		Else
			DebugLog.WriteLine(" ")
		End If
	End If
	WSHLog "Information", "Log tracing commencing for " & DEPLOYNAME & SIDE & RELEASE
	DebugLog.WriteLine(vDateTime & "__ Log tracing commencing for " & DEPLOYNAME & SIDE & RELEASE & " on " & oProcessEnv("COMPUTERNAME"))
	DebugLog.WriteLine(Now & "__ Script/Path utilized: " & Wscript.ScriptFullName)
	If sCoreBlat <> "" Then	DebugLog.WriteLine(sCoreBlat)
	If sCoreBlat2 <> "" Then DebugLog.WriteLine(sCoreBlat2)
	If sCoreWMI <> "" Then DebugLog.WriteLine(sCoreWMI)
	If sCoreInfo <> "" Then DebugLog.WriteLine(sCoreInfo)
	If sCoreInfo2 <> "" Then DebugLog.WriteLine(sCoreInfo2)
	If sCoreInfo3 <> "" Then DebugLog.WriteLine(sCoreInfo3)
	Call GetWhoIsRunningScript
	On Error GoTo 0
End Sub

' Creates Corporate Engineering Logs directory(s) for script, package installation, and custom logging if they do not exist
Function CreateLogDirs
	If Not oFSO.FolderExists(sLogDir) Then Call CreateLogDir(sLogDir)
	If Not oFSO.FolderExists(sScriptsDir) Then Call CreateLogDir(sScriptsDir)
	If Not oFSO.FolderExists(sEmailDir) Then Call CreateLogDir(sEmailDir)
End Function

' Create Log directory's requested 
Sub CreateLogDir(sDir)
	On Error Resume Next
	Dim iErrDesc
	oFSO.CreateFolder(sDir)
	If Err.Number = 0 Then
		WSHLog "Success", sDir & " directory Created."
		If sDir = sLogDir Then sCoreInfo = Now & "__ " & sLogDir & " directory Created."
		If sDir = sScriptsDir Then sCoreInfo2 = Now & "__ " & sScriptsDir & " directory Created."
		If sDir = sEmailDir Then sCoreInfo3 = Now & "__ " & sEmailDir & " directory Created."
	Else
		iErrCode = Err.Number
		iErrDesc = CStr(Err.Number) & " " & Err.Description & "."
		Call GetWhoIsRunningScript
		oProcessEnv("SEE_MASK_NOZONECHECKS") = 1
		WSHLog "CriticalFailure", "Could not create " & sDir & " directory, Error # " & iErrDesc
	End If
	On Error GoTo 0
End Sub

' Copy blat file to system to ensure email alerts are available
Function CopyBlat
	On Error Resume Next
	Dim fn, vTimeThis, sLenghtTM, iErrDesc : fn = "blat.exe"
	If Not oFSO.FileExists(sBlat) Then
		If oFSO.FileExists(sLib & fn) Then
			vTimeThis = Now
			oFSO.CopyFile sLib & fn, sSystem32 & fn, True
			If Err.Number = 0 Then
				sLenghtTM = DateDiffToWords(vTimeThis,Now)
				WSHLog "Success", "Copied " & fn & " successfully. Took: " & sLenghtTM & "."
				sCoreBlat2 = Now & "__ Copied " & fn & " successfully. Took: " & sLenghtTM & "."
				If sDebugBlat = "True" Then Email "Copied " & fn & " successfully", "Copied " & fn & " successfully. Took: " & sLenghtTM & "."
			Else
				iErrCode = Err.Number
				iErrDesc = CStr(Err.Number) & " " & Err.Description & "."
				sBlat = sLib & "blat.exe"
				oProcessEnv("SEE_MASK_NOZONECHECKS") = 1
				WSHLog "Failure", "Could not copy " & fn & " from " & sLib & " to: " & sSystem32 & ", Error # " & iErrDesc & ". Will continue with network copy... ||"
				oProcessEnv.Remove("SEE_MASK_NOZONECHECKS")
			End If
		Else
			sCoreBlat = Now & "__ Blat not found, not in: " & sSystem32 & ", nor in: " & sLib & " no email status will be available."
			WSHLog "Failure", "Blat not found, not in: " & sSystem32 & ", nor in: " & sLib & " no email status will be available."
		End If
	End If
	On Error GoTo 0
End Function

' Get AD domain Name
Function GetDomainName
	On Error Resume Next
	Dim Info
	Set Info = CreateObject("AdSystemInfo")
	If Err.Number <> 0 Then
		iErrCode = Err.Number	
		Call CreateLogDirs
		If Not IsObject(DebugLog) Then Call CreateDebugLog("WriteLog")
		WSHLog "CriticalFailure", "Failed to create object AdSystemInfo, error: " & iErrCode
	Else
		sDomFQDN = Info.DomainDNSName
		sDN = Info.ComputerName
	End If
	On Error GoTo 0
End Function

' WSH Application Event Log, logging Function
Function WSHLog(EventType, Desc)
	On Error Resume Next
	If EventType = "Information" Then oShell.LogEvent 4, sScript & Chr(13) & "------------------------------------------------------" & Chr(13) & Desc
	If EventType = "Success" Then oShell.LogEvent 0, sScript & Chr(13) & "------------------------------------------------------" & Chr(13) & Desc
	If EventType = "Warning" Then
		oShell.LogEvent 2, sScript & Chr(13) & "------------------------------------------------------" & Chr(13) & Desc
		WScript.Sleep 500
		'If sCoreBlat = "" Then Email EventType, Desc & " " & sMsg
	End If
	If EventType = "Failure" Then
		oShell.LogEvent 1, sScript & Chr(13) & "------------------------------------------------------" & Chr(13) & Desc
		WScript.Sleep 500
		sTicket = "True"
		If sCoreBlat = "" Then Email EventType, Desc & " " & sMsg
	End If
	If EventType = "CriticalFailure" Then
		oShell.LogEvent 1, sScript & Chr(13) &    "------------------------------------------------------" & Chr(13) & Desc & " Script exiting..."
		WScript.Sleep 500
		If Not((IsNull(iMSIVal) Or IsEmpty(iMSIVal)) Or iMSIVal = 0 Or iMSIVal = 1030) Then
			If IsObject(DebugLog) Then DebugLog.WriteLine(Now & "__ Critical failure, exiting... Wrapper exit code: " & iMSIVal)
			sPilot = "True" : sTicket = "True" ' Force attachment of debuglog since we have a critical failure
			Call Cleanup(sScript & " - " & EventType, Desc & "| |" & sMsg)
			WScript.Quit(iMSIVal)
		ElseIf Not(IsNull(iErrCode) Or IsEmpty(iErrCode)) Then
			If IsObject(DebugLog) Then DebugLog.WriteLine(Now & "__ Critical failure, exiting... Wrapper exit code : " & iErrCode)
			sPilot = "True" : sTicket = "True" ' Force attachment of debuglog since we have a critical failure
			Call Cleanup(sScript & " - " & EventType, Desc & "| |" & sMsg)
			WScript.Quit(iErrCode)
		Else
			If IsObject(DebugLog) Then DebugLog.WriteLine(Now & "__ Critical failure exiting... Wrapper exit code  : " & Err.Number)
			sPilot = "True" : sTicket = "True" ' Force attachment of debuglog since we have a critical failure
			Call Cleanup(sScript & " - " & EventType, Desc & "| |" & sMsg)
			WScript.Quit
		End If
	End If
	On Error GoTo 0
End Function

' EMail Results Function v3
Function Email(Subject, Body)
	On Error Resume Next
	Dim sTmpFile, sP1, sP2, iMail
	If IsNull(sMailbox2) Or IsEmpty(sMailbox2) Or sMailbox2 = "" Then
		sP1 = sMailbox1
	Else
		sP1 = sMailbox1 & "," & sMailbox2
	End If
	If IsNull(sMailbox3) Or IsEmpty(sMailbox3) Or sMailbox3 = "" Then sP1 = sP1 Else sP1 = sP1 & "," & sMailbox3 End If

	sTmpFile = sEmailDir & "\" & oFSO.GetTempName
	oFSO.CreateTextFile sTmpFile
	If Err.Number <> 0 Then
		sP2 = "-log " & Chr(34) & oProcessEnv("TEMP") & "\"
	Else
		oFSO.DeleteFile sTmpFile
		sP2 = "-log " & Chr(34) & sEmailDir & "\"
	End If

	Call Stamp
	If sTicket = "True" Then
		If IsNull(sSD) Or IsEmpty(sSD) Or sSD = "" Then sP1 = sP1 Else sP1 = sSD & "," & sP1 End If
		If IsObject(DebugLog) Then
			iMail = oShell.Run(sBlat & " -body " & Chr(34) &_
			Body & Chr(34) & " -to " & sP1 & " -subject " & Chr(34) & Subject &_
			Chr(34) & " -server " & sRelayHost & " -f " & sFrom & sFQDN & " -attacht " & Chr(34) & sDebugLog & Chr(34) & " -debug " & sP2 & sScript & "_" & altDate & "-" & altTime & ".log" & Chr(34) & " -try " & sTry, 2, True)
		Else
			iMail = oShell.Run(sBlat & " -body " & Chr(34) &_
			Body & Chr(34) & " -to " & sP1 & " -subject " & Chr(34) & Subject &_
			Chr(34) & " -server " & sRelayHost & " -f " & sFrom & sFQDN & " -debug " & sP2 & sScript & "_" & altDate & "-" & altTime & ".log" & Chr(34) & " -try " & sTry, 2, True)
		End If
	ElseIf (sPilot = "True") And (IsObject(DebugLog)) Then
		iMail = oShell.Run(sBlat & " -body " & Chr(34) &_
		Body & Chr(34) & " -to " & sP1 & " -subject " & Chr(34) & Subject &_
		Chr(34) & " -server " & sRelayHost & " -f " & sCN & sFQDN & " -attacht " & Chr(34) & sDebugLog & Chr(34) & " -debug " & sP2 & sScript & "_" & altDate & "-" & altTime & ".log" & Chr(34) & " -try " & sTry, 2, True)
	Else
		iMail = oShell.Run(sBlat & " -body " & Chr(34) &_
		Body & Chr(34) & " -to " & sP1 & " -subject " & Chr(34) & Subject &_
		Chr(34) & " -server " & sRelayHost & " -f " & sCN & sFQDN & " -debug " & sP2 & sScript & "_" & altDate & "-" & altTime & ".log" & Chr(34) & " -try " & sTry, 2, True)
	End If
	On Error Goto 0
End Function

' Date/Time Stamping Function
Function Stamp
	On Error Resume Next
	Dim oSplit
	vDate = FormatDateTime(Now(), vbShortDate)
	altDate = Replace(Date, "/", "_")
	vTime = FormatDateTime(Now(), vbLongTime)
	altTime = Replace(Time, ":", ".")
	oSplit = Split(altTime)
	altTime = oSplit(0) & "." & oSplit(1)
	On Error GoTo 0
End Function

' Time into words Function
Function DateDiffToWords(d1, d2)
	Dim report, seconds
	report = ""
	seconds = abs(datediff("S", d1, d2))
	If seconds <= 0 Then
		report = "0 seconds"
	Else
		seconds = seconds mod (24*60*60)
		'divide by 3600 to get hours
		If seconds >= 3600 Then
			report = report & seconds\(3600) & " hour(s), "
		End If
		'use mod to get remaining seconds and divide to get minutes
		seconds = seconds mod (60*60)
		If seconds >= 60 Then
			report = report & seconds\(60) & " minute(s), "
		End If
		'use mod to get remaining seconds
		seconds = seconds Mod (60)
		report = report & seconds & " second(s)"
	End If
	DateDiffToWords = report
End Function

' Close up logs, objects, etc...
Function Cleanup(Subject, Body)
	' Close log(s)
	If IsObject(DebugLog) Then DebugLog.Close
	If (sPilot = "True" Or sTicket = "True") And (sCoreBlat = "") Then Email Subject, Body
	If oProcessEnv("SEE_MASK_NOZONECHECKS") = "1" Then oProcessEnv.Remove("SEE_MASK_NOZONECHECKS")
	
	'Clean Up
	If sWMIFailed <> "Failed" Then Set oWMI = Nothing
	Set oProcessEnv = Nothing
	Set oFSO = Nothing
	Set oShell = Nothing
End Function

Function Finalize(sCode, sType)
	Dim sTimed, sSpace
	sTimed = "Wrapper execution complete, total execution time: " & DateDiffToWords(vDateTime,Now)
	sMsg = sMsg & "|" & sTimed
	If IsObject(DebugLog) Then DebugLog.WriteLine(Now & "__ " & sTimed)
	WSHLog "Information", sTimed

	If (UCase(sType) = "MSI" Or UCase(sType) = "EXE" Or UCase(sType) = "ERROR") Then
		If (sCode = 0 Or sCode = 3010) Then sMailbox2 = "" : sMailbox3 = ""
		If Not IsObject(DebugLog) Then Call CreateDebugLog("WriteLog")
		If (UCase(sType) = "MSI" Or UCase(sType) = "EXE") Then sSpace = ": "
		If (UCase(sType) = "ERROR") Then sSpace = " : "
		DebugLog.WriteLine(Now & "__ Exiting... Wrapper exit code" & sSpace & sCode)
	Else
		If sCode = 0 Then sMailbox2 = "" : sMailbox3 = ""
		sSpace = "  : "
		If IsObject(DebugLog) Then DebugLog.WriteLine(Now & "__ Exiting... Wrapper exit code" & sSpace & sCode)
	End If
	If (sSuccess = "True") And (sPilot = "False") And (sCoreBlat = "") Then Email sScript & "  Status", "Wrapper exit code" & sSpace & sCode & "| |" & sMsg
	If sReboot = "Yes" Then oShell.Run "shutdown.exe -r -f -t 5", 1, False
	Call Cleanup(sScript & "  Status", "Wrapper exit code" & sSpace & sCode & "|" & sMsg)
	WScript.Quit(sCode)
End Function

' Must sign script

'' SIG '' Begin signature block
'' SIG '' MIITbgYJKoZIhvcNAQcCoIITXzCCE1sCAQExCzAJBgUr
'' SIG '' DgMCGgUAMGcGCisGAQQBgjcCAQSgWTBXMDIGCisGAQQB
'' SIG '' gjcCAR4wJAIBAQQQTvApFpkntU2P5azhDxfrqwIBAAIB
'' SIG '' AAIBAAIBAAIBADAhMAkGBSsOAwIaBQAEFNihc2aDtRfd
'' SIG '' CHZExDc8J15Fd6jZoIIODDCCBBQwggL8oAMCAQICCwQA
'' SIG '' AAAAAS9O4VLXMA0GCSqGSIb3DQEBBQUAMFcxCzAJBgNV
'' SIG '' BAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNh
'' SIG '' MRAwDgYDVQQLEwdSb290IENBMRswGQYDVQQDExJHbG9i
'' SIG '' YWxTaWduIFJvb3QgQ0EwHhcNMTEwNDEzMTAwMDAwWhcN
'' SIG '' MjgwMTI4MTIwMDAwWjBSMQswCQYDVQQGEwJCRTEZMBcG
'' SIG '' A1UEChMQR2xvYmFsU2lnbiBudi1zYTEoMCYGA1UEAxMf
'' SIG '' R2xvYmFsU2lnbiBUaW1lc3RhbXBpbmcgQ0EgLSBHMjCC
'' SIG '' ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJTv
'' SIG '' Zfi1V5+gUw00BusJH7dHGGrL8Fvk/yelNNH3iRq/nrHN
'' SIG '' EkFuZtSBoIWLZFpGL5mgjXex4rxc3SLXamfQu+jKdN6L
'' SIG '' Tw2wUuWQW+tHDvHnn5wLkGU+F5YwRXJtOaEXNsq5oIwb
'' SIG '' TwgZ9oExrWEWpGLmtECew/z7lfb7tS6VgZjg78Xr2AJZ
'' SIG '' eHf3quNSa1CRKcX8982TZdJgYSLyBvsy3RZR+g79ijDw
'' SIG '' Fwmnu/MErquQ52zfeqn078RiJ19vmW04dKoRi9rfxxRM
'' SIG '' 6YWy7MJ9SiaP51a6puDPklOAdPQD7GiyYLyEIACDG6Hu
'' SIG '' tHQFwSmOYtBHsfrwU8wY+S47+XB+tCUCAwEAAaOB5TCB
'' SIG '' 4jAOBgNVHQ8BAf8EBAMCAQYwEgYDVR0TAQH/BAgwBgEB
'' SIG '' /wIBADAdBgNVHQ4EFgQURtg+/9zjvv+D5vSFm7DdatYU
'' SIG '' qcEwRwYDVR0gBEAwPjA8BgRVHSAAMDQwMgYIKwYBBQUH
'' SIG '' AgEWJmh0dHBzOi8vd3d3Lmdsb2JhbHNpZ24uY29tL3Jl
'' SIG '' cG9zaXRvcnkvMDMGA1UdHwQsMCowKKAmoCSGImh0dHA6
'' SIG '' Ly9jcmwuZ2xvYmFsc2lnbi5uZXQvcm9vdC5jcmwwHwYD
'' SIG '' VR0jBBgwFoAUYHtmGkUNl8qJUC99BM00qP/8/UswDQYJ
'' SIG '' KoZIhvcNAQEFBQADggEBAE5eVpAeRrTZSTHzuxc5KBvC
'' SIG '' Ft39QdwJBQSbb7KimtaZLkCZAFW16j+lIHbThjTUF8xV
'' SIG '' OseC7u+ourzYBp8VUN/NFntSOgLXGRr9r/B4XOBLxRjf
'' SIG '' OiQe2qy4qVgEAgcw27ASXv4xvvAESPTwcPg6XlaDzz37
'' SIG '' Dbz0xe2XnbnU26UnhOM4m4unNYZEIKQ7baRqC6GD/Sjr
'' SIG '' 2u8o9syIXfsKOwCr4CHr4i81bA+ONEWX66L3mTM1fsua
'' SIG '' irtFTec/n8LZivplsm7HfmX/6JLhLDGi97AnNkiPJm87
'' SIG '' 7k12H3nD5X+WNbwtDswBsI5//1GAgKeS1LNERmSMh08W
'' SIG '' YwcxS2Ow3/MwggSfMIIDh6ADAgECAhIRIdaZp2SXPvH4
'' SIG '' Qn7pGcxTQRQwDQYJKoZIhvcNAQEFBQAwUjELMAkGA1UE
'' SIG '' BhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2Ex
'' SIG '' KDAmBgNVBAMTH0dsb2JhbFNpZ24gVGltZXN0YW1waW5n
'' SIG '' IENBIC0gRzIwHhcNMTYwNTI0MDAwMDAwWhcNMjcwNjI0
'' SIG '' MDAwMDAwWjBgMQswCQYDVQQGEwJTRzEfMB0GA1UEChMW
'' SIG '' R01PIEdsb2JhbFNpZ24gUHRlIEx0ZDEwMC4GA1UEAxMn
'' SIG '' R2xvYmFsU2lnbiBUU0EgZm9yIE1TIEF1dGhlbnRpY29k
'' SIG '' ZSAtIEcyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
'' SIG '' CgKCAQEAsBeuotO2BDBWHlgPse1VpNZUy9j2czrsXV6r
'' SIG '' Jf02pfqEw2FAxUa1WVI7QqIuXxNiEKlb5nPWkiWxfSPj
'' SIG '' BrOHOg5D8NcAiVOiETFSKG5dQHI88gl3p0mSl9RskKB2
'' SIG '' p/243LOd8gdgLE9YmABr0xVU4Prd/4AsXximmP/Uq+yh
'' SIG '' RVmyLm9iXeDZGayLV5yoJivZF6UQ0kcIGnAsM4t/aIAq
'' SIG '' taFda92NAgIpA6p8N7u7KU49U5OzpvqP0liTFUy5LauA
'' SIG '' o6Ml+6/3CGSwekQPXBDXX2E3qk5r09JTJZ2Cc/os+XKw
'' SIG '' qRk5KlD6qdA8OsroW+/1X1H0+QrZlzXeaoXmIwRCrwID
'' SIG '' AQABo4IBXzCCAVswDgYDVR0PAQH/BAQDAgeAMEwGA1Ud
'' SIG '' IARFMEMwQQYJKwYBBAGgMgEeMDQwMgYIKwYBBQUHAgEW
'' SIG '' Jmh0dHBzOi8vd3d3Lmdsb2JhbHNpZ24uY29tL3JlcG9z
'' SIG '' aXRvcnkvMAkGA1UdEwQCMAAwFgYDVR0lAQH/BAwwCgYI
'' SIG '' KwYBBQUHAwgwQgYDVR0fBDswOTA3oDWgM4YxaHR0cDov
'' SIG '' L2NybC5nbG9iYWxzaWduLmNvbS9ncy9nc3RpbWVzdGFt
'' SIG '' cGluZ2cyLmNybDBUBggrBgEFBQcBAQRIMEYwRAYIKwYB
'' SIG '' BQUHMAKGOGh0dHA6Ly9zZWN1cmUuZ2xvYmFsc2lnbi5j
'' SIG '' b20vY2FjZXJ0L2dzdGltZXN0YW1waW5nZzIuY3J0MB0G
'' SIG '' A1UdDgQWBBTUooRKOFoYf7pPMFC9ndV6h9YJ9zAfBgNV
'' SIG '' HSMEGDAWgBRG2D7/3OO+/4Pm9IWbsN1q1hSpwTANBgkq
'' SIG '' hkiG9w0BAQUFAAOCAQEAj6kakW0EpjcgDoOW3iPTa24f
'' SIG '' bt1kPWghIrX4RzZpjuGlRcckoiK3KQnMVFquxrzNY46z
'' SIG '' PVBI5bTMrs2SjZ4oixNKEaq9o+/Tsjb8tKFyv22XY3mM
'' SIG '' RLxwL37zvN2CU6sa9uv6HJe8tjecpBwwvKu8LUc235Ig
'' SIG '' A+hxxlj2dQWaNPALWVqCRDSqgOQvhPZHXZbJtsrKnbem
'' SIG '' uuRQ09Q3uLogDtDTkipbxFm7oW3bPM5EncE4Kq3jjb3N
'' SIG '' CXcaEL5nCgI2ZIi5sxsm7ueeYMRGqLxhM2zPTrmcuWrw
'' SIG '' nzf+tT1PmtNN/94gjk6Xpv2fCbxNyhh2ybBNhVDygNId
'' SIG '' BvVYBAexGDCCBU0wggQ1oAMCAQICEAPq4x/DNeCiI9mC
'' SIG '' 5DlK/ZUwDQYJKoZIhvcNAQELBQAwcjELMAkGA1UEBhMC
'' SIG '' VVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UE
'' SIG '' CxMQd3d3LmRpZ2ljZXJ0LmNvbTExMC8GA1UEAxMoRGln
'' SIG '' aUNlcnQgU0hBMiBBc3N1cmVkIElEIENvZGUgU2lnbmlu
'' SIG '' ZyBDQTAeFw0xODAzMjIwMDAwMDBaFw0yMTAzMjUxMjAw
'' SIG '' MDBaMIGJMQswCQYDVQQGEwJVUzERMA8GA1UECBMITmV3
'' SIG '' IFlvcmsxETAPBgNVBAcTCE5ldyBZb3JrMSkwJwYDVQQK
'' SIG '' EyBUaGUgT3JjaGFyZCBFbnRlcnByaXNlcyBOWSwgSW5j
'' SIG '' LjEpMCcGA1UEAxMgVGhlIE9yY2hhcmQgRW50ZXJwcmlz
'' SIG '' ZXMgTlksIEluYy4wggEiMA0GCSqGSIb3DQEBAQUAA4IB
'' SIG '' DwAwggEKAoIBAQC+uBrLcB6HSH12Fw3iQk0wnrBMR/Xm
'' SIG '' yH+oInoXTnQhhGhTle6PWioUUp4b7K6v1o5t/7oqqeU1
'' SIG '' TlZhFrXw20wGa0X5B2U07eeRyB9jWMyeDalbWPazUlfo
'' SIG '' gmtiGnHUTbj1+MtjtnIb2eVv7umcNoi7qhPzJvVMDIyC
'' SIG '' J4OYi6pIfV+nVjuGizGK3Ep43fj8eM1rpvpF4qxsPt/8
'' SIG '' mGgcbZVKVPERkezu8PhnqsQq6BKp6ncq+BbKaolzwRcV
'' SIG '' i7ZZzmpsnRFKU6jE06s0ptBmUCl/7WY69qMdO0Tu4RNY
'' SIG '' ItuihrFaWL2WLdjPANvTFG2xL2DgIz4LEWgSUVOu4aSw
'' SIG '' g39XAgMBAAGjggHFMIIBwTAfBgNVHSMEGDAWgBRaxLl7
'' SIG '' KgqjpepxA8Bg+S32ZXUOWDAdBgNVHQ4EFgQUFWb1jhPM
'' SIG '' z9PmvFrOw3FSTHHXyswwDgYDVR0PAQH/BAQDAgeAMBMG
'' SIG '' A1UdJQQMMAoGCCsGAQUFBwMDMHcGA1UdHwRwMG4wNaAz
'' SIG '' oDGGL2h0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9zaGEy
'' SIG '' LWFzc3VyZWQtY3MtZzEuY3JsMDWgM6Axhi9odHRwOi8v
'' SIG '' Y3JsNC5kaWdpY2VydC5jb20vc2hhMi1hc3N1cmVkLWNz
'' SIG '' LWcxLmNybDBMBgNVHSAERTBDMDcGCWCGSAGG/WwDATAq
'' SIG '' MCgGCCsGAQUFBwIBFhxodHRwczovL3d3dy5kaWdpY2Vy
'' SIG '' dC5jb20vQ1BTMAgGBmeBDAEEATCBhAYIKwYBBQUHAQEE
'' SIG '' eDB2MCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdp
'' SIG '' Y2VydC5jb20wTgYIKwYBBQUHMAKGQmh0dHA6Ly9jYWNl
'' SIG '' cnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFNIQTJBc3N1
'' SIG '' cmVkSURDb2RlU2lnbmluZ0NBLmNydDAMBgNVHRMBAf8E
'' SIG '' AjAAMA0GCSqGSIb3DQEBCwUAA4IBAQAgdQ4jSO4z+O79
'' SIG '' 4AlCpzGIOmn8XT2WTDPXg5b5lPg3Z22YMQ5JHmRzWXax
'' SIG '' mxFAhD8kCSPGhL+MfIJMZt9hTbdM8WUinqKLNvbVDiIK
'' SIG '' O1H9Vnwz5VJ03qGRExixZOiULeeNDmj6kh4KcqFtzio0
'' SIG '' M/4GARMYiwyBg1CNlD59Jvz3RzFZugm3K+yXU1JlznMJ
'' SIG '' 8QoY0MWHsiHV/nJssNnMWCpvR7S0wC5pSn3DXeS7XVma
'' SIG '' SAc3xavwCg5sJG3LS2LKwx12ksbj+mfz+t2RqQIE5iFz
'' SIG '' 81EVhK7qbF8cbRKhgtcqbCjqr3dsj4lfMpe/+OcXR7DI
'' SIG '' wWGGyquVUv0vqiaTph2zMYIEzjCCBMoCAQEwgYYwcjEL
'' SIG '' MAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IElu
'' SIG '' YzEZMBcGA1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTExMC8G
'' SIG '' A1UEAxMoRGlnaUNlcnQgU0hBMiBBc3N1cmVkIElEIENv
'' SIG '' ZGUgU2lnbmluZyBDQQIQA+rjH8M14KIj2YLkOUr9lTAJ
'' SIG '' BgUrDgMCGgUAoHgwGAYKKwYBBAGCNwIBDDEKMAigAoAA
'' SIG '' oQKAADAZBgkqhkiG9w0BCQMxDAYKKwYBBAGCNwIBBDAc
'' SIG '' BgorBgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAjBgkq
'' SIG '' hkiG9w0BCQQxFgQUI4dxZ9+6nMAXzYDQLAl53ZKZj1sw
'' SIG '' DQYJKoZIhvcNAQEBBQAEggEAuyS5c6OyfkKlePlnmNPc
'' SIG '' Z74oT24Z1oTOQz2jpD93G7ZcPeg2EwmmU57ktSwvF3Ju
'' SIG '' +6GJu2iFh5hZ2qGAjyFzlJDBIkcbZ0+ozo6KdVvaVFvA
'' SIG '' yZXG6aXa5opZzh45dhrzel4a0Vz6nwOfa/t/VLg29P4/
'' SIG '' kVJRNVL6haLk2a2S8HPPZKYWGXIuqAV3f6WuTQnZMBjk
'' SIG '' Uz/7Y6EIuOkRzgTHbYNFiXJo1ayCOwsjfwSpMoPynDxA
'' SIG '' 1tB4wx/qJNGJu5bvgLPA5wuTc7PKvgugwdYit4M+ina5
'' SIG '' OWA4sSPADAA/n9omHSVonPXLcOPJ3zFMQBSw9oeVSBpD
'' SIG '' E8bdCvDXYOvjkaGCAqIwggKeBgkqhkiG9w0BCQYxggKP
'' SIG '' MIICiwIBATBoMFIxCzAJBgNVBAYTAkJFMRkwFwYDVQQK
'' SIG '' ExBHbG9iYWxTaWduIG52LXNhMSgwJgYDVQQDEx9HbG9i
'' SIG '' YWxTaWduIFRpbWVzdGFtcGluZyBDQSAtIEcyAhIRIdaZ
'' SIG '' p2SXPvH4Qn7pGcxTQRQwCQYFKw4DAhoFAKCB/TAYBgkq
'' SIG '' hkiG9w0BCQMxCwYJKoZIhvcNAQcBMBwGCSqGSIb3DQEJ
'' SIG '' BTEPFw0yMDA5MzAxNTEyMjJaMCMGCSqGSIb3DQEJBDEW
'' SIG '' BBRGQwF+o73KIrp//HwHUXCoGpe/gjCBnQYLKoZIhvcN
'' SIG '' AQkQAgwxgY0wgYowgYcwgYQEFGO4L6th9YOQlpUFCwAk
'' SIG '' nFApM+x5MGwwVqRUMFIxCzAJBgNVBAYTAkJFMRkwFwYD
'' SIG '' VQQKExBHbG9iYWxTaWduIG52LXNhMSgwJgYDVQQDEx9H
'' SIG '' bG9iYWxTaWduIFRpbWVzdGFtcGluZyBDQSAtIEcyAhIR
'' SIG '' IdaZp2SXPvH4Qn7pGcxTQRQwDQYJKoZIhvcNAQEBBQAE
'' SIG '' ggEAIk+AAaV6E/bQwO4rw+1Hu2uHVEShx0Od6e2FEG5S
'' SIG '' O7M2sOIoujwpcLjqBJQvfZAcJ60t1BgPOYxR7fJDTqYy
'' SIG '' +PFO6ztwb9xH68mp9/uRrgXlSnBUVPhrYGEm6gbzLrmK
'' SIG '' 1TkC+M9OXCsR9OVODQ+cwc1YWcXWTsYkf0uFklCPwKUN
'' SIG '' jOvlm4haZ34k9l4A3HGuJG2IyQ67KSQEu6VckE6qMWM8
'' SIG '' lr9wjQVrgrTfNJjTffaxXGpmKFOQ24oNt4meNbh8z+EI
'' SIG '' DxOA38pOrw3w5n2VgGeNOarXZ9WF/6V17M89UfrfwIQe
'' SIG '' yuTS8IH3Jqy4GITFvcbhNIzq8DK3S1vZU3qFuA==
'' SIG '' End signature block
