Jump to content

Help required with script to schedule server restarts after updates


Recommended Posts

Posted

Hi,

To help be a bit more efficient in doing server windows updates and scheduling reboots of a night I found this script that asks for a date and time and then works out the shutdown -r -t time for and runs the command.

I would like to just enter the time and for it to automatically know that I want tomorrows date as I'll set a time after 0:00.

I don't know that much about powershell but i think i need to get the current date and +1 and inject it into my $RestartDate

Can anyone help with modifying the code below?

Thank you :)

 

[CmdletBinding()]
Param(
[Parameter(Mandatory=$true,HelpMessage="RestartDate Format: dd-MM-yyyy HH:mm")]
[ValidateScript({[DateTime]::ParseExact($_, "dd-MM-yyyy HH:mm", $null)})]
$RestartDate,
[Parameter(Mandatory=$true)]
[string]$Reason
)

$now = get-date
$PostponeTo = get-date($RestartDate)
$secToRestartTimeFromNow = $PostponeTo - $now

$sec = [math]::round($secToRestartTimeFromNow.TotalSeconds)
$cmd = "shutdown /r /t $sec /d P:0:0 /c '$reason'"

Write-Host "Running:"
$cmd
Invoke-Expression -Command $cmd -Verbose
Write-Host
Write-Host "Use following command to cancel: "
Write-Host "shutdown /a"

Posted (edited)

This spits out tomorrows date for me.

$RestartDate = [DateTime]::Today.AddDays(+1).ToString([font=inherit]"dd-MM-yyyy"[/font])

 

Is there any reason you don't just use the task scheduler?

Edited by Rob_D
Posted

I have a scheduled task on most of the servers to do this.

It's a Basic task that runs Powershell.exe, with the additional arguments

restart-computer -Force

 

I set the trigger to be once only at a specific time, and then alter the date I want it to run based on when I've applied the patches.

Posted
This spits out tomorrows date for me.

$RestartDate = [DateTime]::Today.AddDays(+1).ToString([font=inherit]"dd-MM-yyyy"[/font])

 

Is there any reason you don't just use the task scheduler?

 

Thanks I'll give that a go.

The network manager who has since left would use an app called PC Sleep and I'm not sure how good this tool is. It's extremely slow to start up and I had in mind something fast that I just double click on the desktop to schedule reboot.

Task scheduler would take longer setting up on each server, if I understand it correct?

I have about 28 VM I have to run windows updates on every month.

Posted
Used to use Right Click Tools in SCCM, but now we are decommissioning it i use a mix of Windows Update on the server and just setting a restart time, or via Windows Admin Center and just entering a time
Posted
Just a couple of observations here, in your code @mikeglover the write-host no use what so ever,unless someone is sat running the script but the you would not be looking to automate this. The second is a note about just letting servers reboot you should define matanance windows and ensure that the servers are rebooted within those windows and as already noted don't reboot your DC's/ highly available services within one matanance window! And make sure you are capturing when servers are rebooting and why if at all possible.
Posted

So the code below is what we have in place we use a function (Get-PendingReboot - by Brian Wilhite) this is distributed as a schedule task to our servers and is run at the end of the maintenance window (we have a similar script that runs each night that just emails the list of servers that require a reboot). The script also has a reg key that is updated via group policy if we want the script to not reboot a server/ group of servers.

 

By using this we are capturing the reboots of servers and this helps us identify servers that are rebooted outside of their maintenance window. The next update to this is to write out an event to the application log so that we can surface these reboots in our other monitoring tools.

 


Function Get-PendingReboot 
{ 
<# 
.SYNOPSIS 
   Gets the pending reboot status on a local or remote computer. 

.DESCRIPTION 
   This function will query the registry on a local or remote computer and determine if the 
   system is pending a reboot, from either Microsoft Patching or a Software Installation. 
   For Windows 2008+ the function will query the CBS registry key as another factor in determining 
   pending reboot state.  "PendingFileRenameOperations" and "Auto Update\RebootRequired" are observed 
   as being consistant across Windows Server 2003 & 2008. 
  
   CBServicing = Component Based Servicing (Windows 2008) 
   WindowsUpdate = Windows Update / Auto Update (Windows 2003 / 2008) 
   CCMClientSDK = SCCM 2012 Clients only (DetermineIfRebootPending method) otherwise $null value 
   PendFileRename = PendingFileRenameOperations (Windows 2003 / 2008) 

.PARAMETER ComputerName 
   A single Computer or an array of computer names.  The default is localhost ($env:COMPUTERNAME). 

.PARAMETER ErrorLog 
   A single path to send error data to a log file. 

.EXAMPLE 
   PS C:\> Get-PendingReboot -ComputerName (Get-Content C:\ServerList.txt) | Format-Table -AutoSize 
  
   Computer CBServicing WindowsUpdate CCMClientSDK PendFileRename PendFileRenVal RebootPending 
   -------- ----------- ------------- ------------ -------------- -------------- ------------- 
   DC01     False   False           False      False 
   DC02     False   False           False      False 
   FS01     False   False           False      False 

   This example will capture the contents of C:\ServerList.txt and query the pending reboot 
   information from the systems contained in the file and display the output in a table. The 
   null values are by design, since these systems do not have the SCCM 2012 client installed, 
   nor was the PendingFileRenameOperations value populated. 

.EXAMPLE 
   PS C:\> Get-PendingReboot 
  
   Computer     : WKS01 
   CBServicing  : False 
   WindowsUpdate      : True 
   CCMClient    : False 
   PendComputerRename : False 
   PendFileRename     : False 
   PendFileRenVal     :  
   RebootPending      : True 
  
   This example will query the local machine for pending reboot information. 
  
.EXAMPLE 
   PS C:\> $Servers = Get-Content C:\Servers.txt 
   PS C:\> Get-PendingReboot -Computer $Servers | Export-Csv C:\PendingRebootReport.csv -NoTypeInformation 
  
   This example will create a report that contains pending reboot information. 

.LINK 
   Component-Based Servicing: 
   http://technet.microsoft.com/en-us/library/cc756291(v=WS.10).aspx 
  
   PendingFileRename/Auto Update: 
   http://support.microsoft.com/kb/2723674 
   http://technet.microsoft.com/en-us/library/cc960241.aspx 
   http://blogs.msdn.com/b/hansr/archive/2006/02/17/patchreboot.aspx 

   SCCM 2012/CCM_ClientSDK: 
   http://msdn.microsoft.com/en-us/library/jj902723.aspx 

.NOTES 
   Author:  Brian Wilhite 
   Email:   bcwilhite (at) live.com 
   Date:    29AUG2012 
   PSVer:   2.0/3.0/4.0/5.0 
   Updated: 01DEC2014 
   UpdNote: Added CCMClient property - Used with SCCM 2012 Clients only 
      Added ValueFromPipelineByPropertyName=$true to the ComputerName Parameter 
      Removed $Data variable from the PSObject - it is not needed 
      Bug with the way CCMClientSDK returned null value if it was false 
      Removed unneeded variables 
      Added PendFileRenVal - Contents of the PendingFileRenameOperations Reg Entry 
      Removed .Net Registry connection, replaced with WMI StdRegProv 
      Added ComputerPendingRename 
#> 

[CmdletBinding()] 
param( 
 [Parameter(Position=0,ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true)] 
 [Alias("CN","Computer")] 
 [string[]]$ComputerName="$env:COMPUTERNAME", 
 [string]$ErrorLog 
 ) 

Begin {  }## End Begin Script Block 
Process { 
 Foreach ($Computer in $ComputerName) { 
 Try { 
     ## Setting pending values to false to cut down on the number of else statements 
     $CompPendRen,$PendFileRename,$Pending,$SCCM = $false,$false,$false,$false 
      
     ## Setting CBSRebootPend to null since not all versions of Windows has this value 
     $CBSRebootPend = $null 
            
     ## Querying WMI for build version 
     $WMI_OS = Get-WmiObject -Class Win32_OperatingSystem -Property BuildNumber, CSName -ComputerName $Computer -ErrorAction Stop 

     ## Making registry connection to the local/remote computer 
     $HKLM = [uInt32] "0x80000002" 
     $WMI_Reg = [WMIClass] "\\$Computer\root\default:StdRegProv" 
            
     ## If Vista/2008 & Above query the CBS Reg Key 
     If ([int32]$WMI_OS.BuildNumber -ge 6001) { 
       $RegSubKeysCBS = $WMI_Reg.EnumKey($HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\Component Based Servicing\") 
       $CBSRebootPend = $RegSubKeysCBS.sNames -contains "RebootPending"     
     } 
              
     ## Query WUAU from the registry 
     $RegWUAURebootReq = $WMI_Reg.EnumKey($HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update\") 
     $WUAURebootReq = $RegWUAURebootReq.sNames -contains "RebootRequired" 
            
     ## Query PendingFileRenameOperations from the registry 
     $RegSubKeySM = $WMI_Reg.GetMultiStringValue($HKLM,"SYSTEM\CurrentControlSet\Control\Session Manager\","PendingFileRenameOperations") 
     #$RegValuePFRO = $RegSubKeySM.sValue 
     $RegValuePFRO = @($RegSubKeySM.sValue).Where({$_ -ne ""})
     ## Query ComputerName and ActiveComputerName from the registry 
     $ActCompNm = $WMI_Reg.GetStringValue($HKLM,"SYSTEM\CurrentControlSet\Control\ComputerName\ActiveComputerName\","ComputerName")       
     $CompNm = $WMI_Reg.GetStringValue($HKLM,"SYSTEM\CurrentControlSet\Control\ComputerName\ComputerName\","ComputerName") 
     If ($ActCompNm -ne $CompNm) { 
     $CompPendRen = $true 
     } 
            
     ## If PendingFileRenameOperations has a value set $RegValuePFRO variable to $true 
     $PendFileRename = $false
     $noRebootFiles = @()
     If ($RegValuePFRO)
     {
     foreach($regValue in $RegValuePFRO)
     {
       If ($RegValue -like "*\C:\windows\system32\spool\*" -or $RegValue -like "*\C:\Program Files (x86)\Google\*" -or $RegValue -like "*\C:\Users\*")
       {
          $noRebootFiles += $regValue
       }
       else
       {
       $PendFileRename = $true
       }
       }

     }

     $manualReboot = $false
     If (Test-Path c:\temp\RebootRequired.txt)
     {

     $loops = 0

     Do{
     
     Start-Sleep -Seconds 5
     Remove-Item c:\temp\RebootRequired.txt -Force
     $loops++

     }
     Until (!(Test-Path c:\temp\RebootRequired.txt) -or $loops -gt 12)

     $manualReboot = $true

     }
     

     ## Determine SCCM 2012 Client Reboot Pending Status 
     ## To avoid nested 'if' statements and unneeded WMI calls to determine if the CCM_ClientUtilities class exist, setting EA = 0 
     $CCMClientSDK = $null 
     $CCMSplat = @{ 
   NameSpace='ROOT\ccm\ClientSDK' 
   Class='CCM_ClientUtilities' 
   Name='DetermineIfRebootPending' 
   ComputerName=$Computer 
   ErrorAction='Stop' 
     } 
     ## Try CCMClientSDK 
     Try { 
   $CCMClientSDK = Invoke-WmiMethod @CCMSplat 
     } Catch [system.UnauthorizedAccessException] { 
   $CcmStatus = Get-Service -Name CcmExec -ComputerName $Computer -ErrorAction SilentlyContinue 
   If ($CcmStatus.Status -ne 'Running') { 
       Write-Warning "$Computer`: Error - CcmExec service is not running." 
       $CCMClientSDK = $null 
   } 
     } Catch { 
   $CCMClientSDK = $null 
     } 

     If ($CCMClientSDK) { 
   If ($CCMClientSDK.ReturnValue -ne 0) { 
     Write-Warning "Error: DetermineIfRebootPending returned error code $($CCMClientSDK.ReturnValue)"     
       } 
       If ($CCMClientSDK.IsHardRebootPending -or $CCMClientSDK.RebootPending) { 
     $SCCM = $true 
       } 
     } 
      
     Else { 
   $SCCM = $null 
     } 

     ## Creating Custom PSObject and Select-Object Splat 
     $SelectSplat = @{ 
   Property=( 
       'Computer', 
       'CBServicing', 
       'WindowsUpdate', 
       'CCMClientSDK', 
       'PendComputerRename', 
       'PendFileRename', 
       'PendFileRenVal', 
       'NoRebootFiles',
       'RebootPending' 
   )} 
     New-Object -TypeName PSObject -Property @{ 
   Computer=$WMI_OS.CSName 
   CBServicing=$CBSRebootPend 
   WindowsUpdate=$WUAURebootReq 
   CCMClientSDK=$SCCM 
   PendComputerRename=$CompPendRen 
   PendFileRename=$PendFileRename 
   PendFileRenVal=$RegValuePFRO
   NoRebootFiles=$noRebootFiles
   #PendFileRenVal=$noRebootFiles
   RebootPending=($CompPendRen -or $CBSRebootPend -or $WUAURebootReq -or $SCCM -or $PendFileRename -or $manualReboot)
    
     } | Select-Object @SelectSplat 

 } Catch { 
     Write-Warning "$Computer`: $_" 
     ## If $ErrorLog, log the file to a user specified location/path 
     If ($ErrorLog) { 
   Out-File -InputObject "$Computer`,$_" -FilePath $ErrorLog -Append 
     }         
 }       
 }## End Foreach ($Computer in $ComputerName)       
}## End Process 

End {  }## End End 

}## End Function Get-PendingReboot


################# test server for reboot status and restart if true ########################################

################# Set Mail server SMTP Address #############################################################

$PSEmailServer = '%YourMAilServerHere%'

############################################################################################################

###################################### Vars ################################################################

$rb = Get-PendingReboot
$strSrv = $env:COMPUTERNAME
$strDate = Get-Date -Format dd/MM/yy-h:mm:ss

   $props = @{
       CBServicing = "$($RB.CBServicing)"
       WindowsUpdate = "$($RB.WindowsUpdate)"
       PendComputerRename = "$($RB.PendComputerRename)"
       PendFileRename = "$($RB.PendFileRename)"
       PendFileRenVal = "$($RB.PendFileRenVal)"
       NoRebootFiles = "$($RB.NoRebootFiles)"
       RebootPending = "$($RB.rebootpending)"
       ManualRebootReq = "$manualReboot"
   }

   $objRBP = new-object psobject -Property $props

$strMailB1 = $objRBPList | ? {$_.RebootPending -eq "True"} | select RebootPending,CBServicing,WindowsUpdate,PendComputerRename,PendFileRename,PendFileRenVal

$strMailB = $strSrv + " Server was restarted by the Restart Computer with Reg Check script at " + $strDate + "`n" + "Restart Reason `n" + "CBServicing : " + $objRBP.CBServicing + "`n" + "Windows Update : " + $objRBP.WindowsUpdate + "`n" + "Pending Computer Rename : " + $objRBP.PendComputerRename + "`n" + "Pending File Rename : " + $objRBP.PendFileRename + "`n" + "Pending File Rename Value : " + $objRBP.PendFileRenVal
$strMailSub = $strSrv +  " restarted by script"
$strMailSubNoReboot = $strSrv +  " has not restarted (pending file rename operation)"
$strMailBNoReboot = $strSrv + " Server was not restarted by the Restart Computer with Reg Check script at " + $strDate + "`n" + "`n" + "Pending File Rename Value : " + "`n" + $objRBP.NoRebootFiles
$strMailBMan = $strSrv + " Server has not deleted its Manual Reboot File, please investigate at " + $strDate
$strMailSubMan = $strSrv +  " rebooted but didn't remove its Manual Reboot File"
$strMailBForceNoReboot = $strSrv + " Server was not restarted by the Restart Computer with Reg Check script at " + $strDate + "`n" + "`n" + "Pending reboot Reason `n" + "CBServicing : " + $objRBP.CBServicing + "`n" + "Windows Update : " + $objRBP.WindowsUpdate + "`n" + "Pending Computer Rename : " + $objRBP.PendComputerRename + "`n" + "Pending File Rename : " + $objRBP.PendFileRename + "`n" + "Pending File Rename Value : " + $objRBP.PendFileRenVal + "`n" + "`n" + "This was due to the No Reboot registry value being set"
$strMailSubForceNoReboot = $strSrv +  " has not restarted (registry entry)"

if ($rb.RebootPending -eq $true){

If(Test-Path -Path Registry::HKEY_LOCAL_MACHINE\SOFTWARE\PendingRebootCheck\PendingReboot){

$AutoRebootDisabledReg = Get-Item -Path Registry::HKEY_LOCAL_MACHINE\SOFTWARE\PendingRebootCheck\PendingReboot
$AutoRebootDisabled = $AutoRebootDisabledReg.GetValue('AutoRebootDisabled')

If($AutoRebootDisabled -eq '1'){

Send-MailMessage -to "YourITTeamHere" -from "[email protected]" -Subject $strMailSubForceNoReboot -Body $strMailBForceNoReboot
Exit

}

}

#the line below determines the number of seconds to sleep between the value after minimum and the value after maximum
$sleepval = get-random -minimum 0 -maximum 600

#the line below sleeps the script for a number of seconds equal to sleep value
Start-sleep -Seconds $sleepval

Send-MailMessage -to "YourITTeamHere" -from "[email protected]" -Subject $strMailSub -Body $strMailB

If($loops -gt 12)
{

Send-MailMessage -to "YourITTeamHere" -from "[email protected]" -Subject $strMailSubMan -Body $strMailBMan

}

Start-Sleep -Seconds 3

Restart-Computer -Force
}
elseif ($rb.PendFileRenVal)
{
Send-MailMessage -to "YourITTeamHere" -from "[email protected]" -Subject $strMailSubNoReboot -Body $strMailBNoReboot

}

  • Thanks 1
Posted
The second is a note about just letting servers reboot you should define matanance windows and ensure that the servers are rebooted within those windows and as already noted don't reboot your DC's/ highly available services within one matanance window! And make sure you are capturing when servers are rebooting and why if at all possible.

 

Erm, I'm trying to script reboots by a defining a time when I set so I see that as maintenance rather than "just letting servers reboot". Pretty much every cumulative update I've seen on a server always wants a reboot afterwards so I don't see this as strange behaviour to need to reboot a server.

Posted
Sorry the point I was trying to make there is when you might not have control of the server as some tools/ ransome ware requires a reboot and being able to identify reboots outside of your knowns as it were is useful...
Posted
Sorry the point I was trying to make there is when you might not have control of the server as some tools/ ransome ware requires a reboot and being able to identify reboots outside of your knowns as it were is useful...

 

Thanks HPlum78. Due to circumstances I've ended up solely managing and updating these servers and the program that was was used before was a PC Sleep app tool installed on all the servers that basically did a reboot at a defined time.

I think I need to take a step back and look at how others deploy Windows Updates on servers then as I don't think I have maintenance windows setup. Currently I remote into a server set windows updates off manually and then schedule a reboot at say 3:00am the next day (DC's are different) and then I wait for the next 2nd Tuesday, doing this 28 times is time consuming and was looking at ways of better managing it. I like to script things which is why I went down this avenue.

I like your idea about it emailing what servers need reboots so I'm going to gave a good look.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now



×
×
  • Create New...