From 4d9fe68f66379b59b536ce30eedef01f7a5cafb6 Mon Sep 17 00:00:00 2001 From: Danny de Kooker Date: Wed, 6 Sep 2023 15:47:32 +0200 Subject: [PATCH] Add Task_scripts/Win - Reboot when pending.ps1 --- Task_scripts/Win - Reboot when pending.ps1 | 194 +++++++++++++++++++++ 1 file changed, 194 insertions(+) create mode 100644 Task_scripts/Win - Reboot when pending.ps1 diff --git a/Task_scripts/Win - Reboot when pending.ps1 b/Task_scripts/Win - Reboot when pending.ps1 new file mode 100644 index 0000000..851eb7a --- /dev/null +++ b/Task_scripts/Win - Reboot when pending.ps1 @@ -0,0 +1,194 @@ +<# + .SYNOPSIS + Check if there is a pending reboot on a server and reboot + + .DESCRIPTION + This script if run as a task it will reboot the server/machine if there is a reboot pending on the host, + This script can be used in order to control the reboot schedule for Windows Updates for example. + + .OUTPUTS + This script will reboot the machine when there is a pending reboot. + + Errorcodes: + n/a + + .EXAMPLE + Win - Reboot when pending.ps1 + + .NOTES + Author: D.de Kooker + Source: n/a + + .CHANGELOG + 24-06-2022 - Initial script. +#> + +#region Parameters, functions and global variables + [CmdletBinding()] + param( + # ComputerName is optional. If not specified, localhost is used. + [ValidateNotNullOrEmpty()] + [string[]]$ComputerName, + + [Parameter()] + [ValidateNotNullOrEmpty()] + [pscredential]$Credential + ) + + $ErrorActionPreference = 'Stop' + + $scriptBlock = { + if ($null -ne $using) { + # $using is only available if this is being called with a remote session + $VerbosePreference = $using:VerbosePreference + } + + function Test-RegistryKey { + [OutputType('bool')] + [CmdletBinding()] + param + ( + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string]$Key + ) + + $ErrorActionPreference = 'Stop' + + if (Get-Item -Path $Key -ErrorAction Ignore) { + $true + } + } + + function Test-RegistryValue { + [OutputType('bool')] + [CmdletBinding()] + param + ( + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string]$Key, + + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string]$Value + ) + + $ErrorActionPreference = 'Stop' + + if (Get-ItemProperty -Path $Key -Name $Value -ErrorAction Ignore) { + $true + } + } + + function Test-RegistryValueNotNull { + [OutputType('bool')] + [CmdletBinding()] + param + ( + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string]$Key, + + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string]$Value + ) + + $ErrorActionPreference = 'Stop' + + if (($regVal = Get-ItemProperty -Path $Key -Name $Value -ErrorAction Ignore) -and $regVal.($Value)) { + $true + } + } + + # Added "test-path" to each test that did not leverage a custom function from above since + # an exception is thrown when Get-ItemProperty or Get-ChildItem are passed a nonexistant key path + $tests = @( + { Test-RegistryKey -Key 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Component Based Servicing\RebootPending' } + { Test-RegistryKey -Key 'HKLM:\Software\Microsoft\Windows\CurrentVersion\Component Based Servicing\RebootInProgress' } + { Test-RegistryKey -Key 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update\RebootRequired' } + { Test-RegistryKey -Key 'HKLM:\Software\Microsoft\Windows\CurrentVersion\Component Based Servicing\PackagesPending' } + { Test-RegistryKey -Key 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update\PostRebootReporting' } + #{ Test-RegistryValueNotNull -Key 'HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager' -Value 'PendingFileRenameOperations' } + #{ Test-RegistryValueNotNull -Key 'HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager' -Value 'PendingFileRenameOperations2' } + { + # Added test to check first if key exists, using "ErrorAction ignore" will incorrectly return $true + 'HKLM:\SOFTWARE\Microsoft\Updates' | Where-Object { test-path $_ -PathType Container } | ForEach-Object { + if(Test-Path "$_\UpdateExeVolatile" ){ + (Get-ItemProperty -Path $_ -Name 'UpdateExeVolatile' | Select-Object -ExpandProperty UpdateExeVolatile) -ne 0 + }else{ + $false + } + } + } + { Test-RegistryValue -Key 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnce' -Value 'DVDRebootSignal' } + { Test-RegistryKey -Key 'HKLM:\SOFTWARE\Microsoft\ServerManager\CurrentRebootAttempts' } + { Test-RegistryValue -Key 'HKLM:\SYSTEM\CurrentControlSet\Services\Netlogon' -Value 'JoinDomain' } + { Test-RegistryValue -Key 'HKLM:\SYSTEM\CurrentControlSet\Services\Netlogon' -Value 'AvoidSpnSet' } + { + # Added test to check first if keys exists, if not each group will return $Null + # May need to evaluate what it means if one or both of these keys do not exist + ( 'HKLM:\SYSTEM\CurrentControlSet\Control\ComputerName\ActiveComputerName' | Where-Object { test-path $_ } | % { (Get-ItemProperty -Path $_ ).ComputerName } ) -ne + ( 'HKLM:\SYSTEM\CurrentControlSet\Control\ComputerName\ComputerName' | Where-Object { Test-Path $_ } | % { (Get-ItemProperty -Path $_ ).ComputerName } ) + } + { + # Added test to check first if key exists + 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Services\Pending' | Where-Object { + (Test-Path $_) -and (Get-ChildItem -Path $_) } | ForEach-Object { $true } + } + ) + + foreach ($test in $tests) { + Write-Verbose "Running scriptblock: [$($test.ToString())]" + if (& $test) { + $true + break + } + } + } +#endregion +#region script +# if ComputerName was not specified, then use localhost +# to ensure that we don't create a Session. + if ($null -eq $ComputerName) { + $ComputerName = "localhost" + } + + foreach ($computer in $ComputerName) { + try { + $connParams = @{ + 'ComputerName' = $computer + } + if ($PSBoundParameters.ContainsKey('Credential')) { + $connParams.Credential = $Credential + } + + $output = @{ + ComputerName = $computer + IsPendingReboot = $false + } + + if ($computer -in ".", "localhost", $env:COMPUTERNAME ) { + if (-not ($output.IsPendingReboot = Invoke-Command -ScriptBlock $scriptBlock)) { + $output.IsPendingReboot = $false + } + } + else { + $psRemotingSession = New-PSSession @connParams + + if (-not ($output.IsPendingReboot = Invoke-Command -Session $psRemotingSession -ScriptBlock $scriptBlock)) { + $output.IsPendingReboot = $false + } + } + [pscustomobject]$output.IsPendingReboot + if ($output.IsPendingReboot -eq $true){Restart-Computer -Force} + } catch { + Write-Error -Message $_.Exception.Message + } finally { + if (Get-Variable -Name 'psRemotingSession' -ErrorAction Ignore) { + $psRemotingSession | Remove-PSSession + } + } + } +#endregion \ No newline at end of file