-
-
'Matt Broadstock
'04/22/2011
'www.smsutils.com
'Sample command line:
'cscript "Reboot_Custom.vbs" /PromptIfRebootNeeded:True /RebootForPatches:True /RebootForPendingRenames:False /AlwaysReboot:False /RebootAfterMaxPrompts:False /PromptShowSeconds:600 /PromptInterval:600 /MaxNumberOfPrompts:5
'See Defaults section for the behavior if arguments are not specified
'all arguments are optional but some should be specified for the script to do anything
'Search script for '######## and make sure the lines in between are uncommented (should be 2 of them)
'(This script is being made available in "Safe" mode where it will not actually prompt or reboot without changes)
'Make sure DEBUGMODE = False before using in production
'To use via SMS/SCCM.
'Set to run whether or not a user is logged in
'Allow User to Interact with this program
'Set Max runtime as appropriate (might need to be VERY Long depending on what settings you use)
Option Explicit
Dim WshShell
Dim sComputer, blnIsAnyOneLoggedIn, blnPendingFileRenameOperations
Dim blnPatchesPendingReboot
Dim intNumPatchesPendingReboot
Dim blnPromptIfRebootNeeded, blnRebootForPatches, blnRebootForPendingRenames, blnAlwaysReboot, blnRebootAfterMaxPrompts
Dim intPromptShowSeconds, intPromptInterval, intPromptIntervalMS, intMaxNumberOfPrompts
Dim sAllEvents
'Set Constants
Const HKEY_LOCAL_MACHINE = &H80000002
Const intRebootFlags = 6
Const DEBUGMODE = False
Const LOGTOEVENTLOG = True
'Set Defaults that can be over-ridden by command-line arguments
blnPromptIfRebootNeeded = True 'Should we prompt the user if they are logged in?
blnRebootForPatches = False 'Should we reboot if installed patches are pending reboot?
blnRebootForPendingRenames = False 'Should we reboot if there are Pending File Renames?
blnAlwaysReboot = False 'Should we always reboot?
blnRebootAfterMaxPrompts = False 'Should we reboot after doing the max number of prompts?
intPromptShowSeconds = 600 'How long does the popup window show each time? (in seconds) [Default = 10 minutes]
intPromptInterval = 1800 'Time between popups (in seconds) [Default = 30 minutes]
intMaxNumberOfPrompts = 5 'Number of times to prompt
'''''''''''''''''''''''''''''''''''''''''''''''''
'For testing WMI checks against remote systems
'(Patch Checks will not work remotely)
sComputer = "."
'sComputer = "w009-package4"
'sComputer = "w009-0015"
'sComputer = "OBIX-BS195X5402" 'nopendingrename
'sComputer = "W007-5735XPRMAL" 'pendingrename
'sComputer = "L055-7049XXSMCC" 'pendingrename
'''''''''''''''''''''''''''''''''''''''''''''''''
Set WshShell = CreateObject("WScript.Shell")
Call LogEvent("Starting Custom Shutdown Script")
Call LogEvent("-------------------------------")
On Error Resume Next
Err.Clear
'Get command-line arguments
If WScript.Arguments.Named.Exists("PromptIfRebootNeeded") = True Then blnPromptIfRebootNeeded = CBool(Wscript.Arguments.Named.Item("PromptIfRebootNeeded"))
If WScript.Arguments.Named.Exists("RebootForPatches") = True Then blnRebootForPatches = CBool(Wscript.Arguments.Named.Item("RebootForPatches"))
If WScript.Arguments.Named.Exists("RebootForPendingRenames") = True Then blnRebootForPendingRenames = CBool(Wscript.Arguments.Named.Item("RebootForPendingRenames"))
If WScript.Arguments.Named.Exists("AlwaysReboot") = True Then blnAlwaysReboot = CBool(Wscript.Arguments.Named.Item("AlwaysReboot"))
If WScript.Arguments.Named.Exists("RebootAfterMaxPrompts") = True Then blnRebootAfterMaxPrompts = CBool(Wscript.Arguments.Named.Item("RebootAfterMaxPrompts"))
If WScript.Arguments.Named.Exists("PromptShowSeconds") = True Then intPromptShowSeconds = CInt(Wscript.Arguments.Named.Item("PromptShowSeconds"))
If WScript.Arguments.Named.Exists("PromptInterval") = True Then intPromptInterval = CInt(Wscript.Arguments.Named.Item("PromptInterval"))
If WScript.Arguments.Named.Exists("MaxNumberOfPrompts") = True Then intMaxNumberOfPrompts = CInt(Wscript.Arguments.Named.Item("MaxNumberOfPrompts"))
If Err.Number 0 Then
Call LogEvent("Bad command line specified")
WScript.Quit(1)
End If
On Error GoTo 0
intPromptIntervalMS = intPromptInterval * 1000 'Sleep method uses milliseconds
Call LogEvent("Computer: " & sComputer)
Call LogEvent("PromptIfRebootNeeded: " & blnPromptIfRebootNeeded)
Call LogEvent("RebootForPatches: " & blnRebootForPatches)
Call LogEvent("RebootForPendingRenames: " & blnRebootForPendingRenames)
Call LogEvent("AlwaysReboot: " & blnAlwaysReboot)
Call LogEvent("PromptShowSeconds: " & intPromptShowSeconds)
Call LogEvent("PromptInterval: " & intPromptInterval)
Call LogEvent("MaxNumberOfPrompts: " & intMaxNumberOfPrompts)
Call LogEvent("-------------------------------")
If blnAlwaysReboot = False Then
'Only do these checks if we aren't always forcing the reboot
blnPatchesPendingReboot = ArePatchesPendingReboot
Call LogEvent("-------------------------------")
'Get Pending Patch Reboot by parsing each patch
'intNumPatchesPendingReboot = NumPatchesPendingReboot
'Call LogEvent("NumPatchesPendingReboot:" & vbTab & intNumPatchesPendingReboot)
'If intNumPatchesPendingReboot > 0 Then blnPatchesPendingReboot = True
'Get Pending Patch Reboots using simple check ("Microsoft.Update.SystemInfo" -> RebootRequired)
blnPatchesPendingReboot = ArePatchesPendingReboot
Call LogEvent("PatchesPendingReboot:" & vbTab & vbTab & blnPatchesPendingReboot)
blnPendingFileRenameOperations = CheckIfPendingFileRenameOperations
Call LogEvent("PendingFileRenameOperations:" & vbTab & blnPendingFileRenameOperations)
End If
'Check to see if anyone is logged in
blnIsAnyOneLoggedIn = IsAnyOneLoggedIn
Call LogEvent("IsAnyOneLoggedIn:" & vbTab & vbTab & blnIsAnyOneLoggedIn)
'See if we need to reboot
If (blnRebootForPatches = True And blnPatchesPendingReboot = True) _
Or (blnRebootForPendingRenames = True And blnPendingFileRenameOperations= True) _
Or (blnAlwaysReboot = True) Then
Call LogEvent("Reboot needed:" & vbTab & vbTab & vbTab & "TRUE")
'If anyone is logged in, should we prompt?
If blnIsAnyOneLoggedIn = True And blnPromptIfRebootNeeded = True Then
'Someone is logged in-Prompt for permission to reboot
Call PromptForReboot
Else
'Reboot right away since no one is logged in
Call RebootComputer
End If
Else
Call LogEvent("Reboot needed:" & vbTab & vbTab & vbTab & "FALSE")
End If
If LOGTOEVENTLOG = True Then WshShell.LogEvent 0, sAllEvents
WScript.Quit(0)
'**********************************************************************************
'**********************************************************************************
'**********************************************************************************
Function LogEvent(sEventText)
If DEBUGMODE = True Then WScript.Echo sEventText 'Echo out current Event
sAllEvents = sAllEvents & sEventText & vbCrLf 'Capture all events to write to Event Log
End Function
Function CheckIfPendingFileRenameOperations
Dim oWMIRegistry, intReturn, arrValues
CheckIfPendingFileRenameOperations = False
Const sPendingRenameKeyPath = "SYSTEM\CurrentControlSet\Control\Session Manager"
Const sPendingRenameValue = "PendingFileRenameOperations"
Set oWMIRegistry = GetObject("winmgmts:\\" & sComputer & "\root\default:StdRegProv")
intReturn = oWMIRegistry.GetMultiStringValue(HKEY_LOCAL_MACHINE, sPendingRenameKeyPath, sPendingRenameValue, arrValues)
If IsNull(arrValues) Then
'Call LogEvent("No PendingRenames")
Else
'Call LogEvent("Found PendingRenames")
CheckIfPendingFileRenameOperations = True
End If
End Function
Function ArePatchesPendingReboot
'Simple check for Patches pending reboot (faster)
Dim oSysInfo
Set oSysInfo = CreateObject("Microsoft.Update.SystemInfo")
ArePatchesPendingReboot = oSysInfo.RebootRequired
End Function
Function NumPatchesPendingReboot
'Parse each patch to see if any are pending reboot (slower but can return more info)
Dim updateSession, updateSearcher, searchResult, Update
Dim I, intNumPatchesPendingReboot_sub
intNumPatchesPendingReboot_sub = 0
Set updateSession = CreateObject("Microsoft.Update.Session")
Set updateSearcher = updateSession.CreateupdateSearcher()
updateSearcher.ServerSelection = 2 'Microsoft
Call LogEvent("Searching for updates..." & vbCrLf)
Set searchResult = updateSearcher.Search("IsInstalled=1")
For I = 0 To searchResult.Updates.Count - 1
Dim Category
Set Update = searchResult.Updates.Item(I)
If Update.RebootRequired = True Then
intNumPatchesPendingReboot_sub = intNumPatchesPendingReboot_sub + 1
Call LogEvent(I + 1 & vbTab & Update.MsrcSeverity& vbTab & Update.Type & vbTab & Update.Title)
End If
Next
NumPatchesPendingReboot = intNumPatchesPendingReboot_sub
End Function
Function IsAnyOneLoggedIn
Dim oWMI, colProcesses, oProcess
IsAnyOneLoggedIn = False
Set oWMI = GetObject("winmgmts:{impersonationLevel=impersonate}!\\" & sComputer & "\root\cimv2")
Set colProcesses = oWMI.ExecQuery("Select * From Win32_Process Where Caption='explorer.exe'" )
For Each oProcess in colProcesses
IsAnyOneLoggedIn = True
Next
End Function
Function PromptForReboot
Dim intReturn, intNumberOfPrompts
Call LogEvent("PromptForReboot")
'################################
'Exit Function 'Leave uncommented for testing
'################################
intNumberOfPrompts = 0
'0 Show OK button.
'1 Show OK and Cancel buttons.
'2 Show Abort, Retry, and Ignore buttons.
'3 Show Yes, No, and Cancel buttons.
'4 Show Yes and No buttons.
'5 Show Retry and Cancel buttons.
'Icon Types
'16 Show "Stop Mark" icon.
'32 Show "Question Mark" icon.
'48 Show "Exclamation Mark" icon.
'64 Show "Information Mark" icon.
For intNumberOfPrompts = 1 To intMaxNumberOfPrompts
'intNumberOfPrompts = intNumberOfPrompts + 1
intReturn = WshShell.Popup("This computer needs to Reboot", intPromptShowSeconds, "Reboot Reminder" , 4 + 32)
'7 = No : 6 = Yes : -1 = Popup Timeout
Select Case intReturn
Case 7 'No button clicked
If intNumberOfPrompts => intMaxNumberOfPrompts Then
Call LogEvent(vbTab & "Delayed Reboot max number of times")
If blnRebootAfterMaxPrompts = True Then
WshShell.Popup "Reboot has been delayed the maximum number of times." & vbCrLf & "This computer will Reboot in 5 minutes (or when you click OK)", 300, "MANDATORY REBOOT" , 0
Call RebootComputer
Exit For
Else
'Exit without ever rebooting
Exit For
End If
End If
Call LogEvent(vbTab & "Times Delayed: " & intNumberOfPrompts)
Call LogEvent(vbTab & "Sleeping for " & intPromptInterval & " seconds")
If LOGTOEVENTLOG = True Then WshShell.LogEvent 0, sAllEvents
WshShell.Popup "Reboot reminder will popup again in " & Int(intPromptInterval / 60) & " minutes", 5, "Reboot Reminder" , 0
WScript.Sleep(intPromptIntervalMS)
Case 6 'Yes button clicked
Call RebootComputer
Exit For
Case -1 'Popup timed out
If intNumberOfPrompts => intMaxNumberOfPrompts Then Exit For
Call LogEvent("Sleeping for " & intPromptInterval & " seconds")
WScript.Sleep(intPromptIntervalMS)
Case Else
End Select
Next
End Function
Sub RebootComputer
Dim oWMI, colOperatingSystems, oOS
'Win32ShutdownTracker - Can set timeout but need Vista or newer
' 0 (0x0) Log Off
' 1 (0x1) Shutdown
' 2 (0x2) Reboot
' 4 (0x4) Forced Log Off (0 + 4)
' 5 (0x5) Forced Shutdown (1 + 4)
' 6 (0x6) Forced Reboot (2 + 4)
' 8 (0x8) Power Off
' 12 (0xC) Forced Power Off (8 + 4)
Call LogEvent("REBOOTING NOW")
Set oWMI = GetObject("winmgmts:{impersonationLevel=impersonate,(Shutdown)}!\\" & _
sComputer & "\root\cimv2")
Set colOperatingSystems = oWMI.ExecQuery("Select * from Win32_OperatingSystem WHERE Primary=True")
For Each oOS in colOperatingSystems
'################################
oOS.Win32Shutdown intRebootFlags 'Leave uncommented for testing
'################################
Next
End Sub