narrow default width wide
colour style colour style colour style colour style

Reboot Script

 
'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