Patching silently and installing applications without any user interaction… This is really hard to do and it’s really hard to get compliant machines if you do not force a reboot every now and then.
Then, if you force the user to reboot with a short timeout they will not be to happy.
So… to solve this I took some inspiration from a couple of blogposts some scripts and then some of my knowledge and put together a recipe that I think work’s really nice. đ
All in all, this results in a nice popup (only when a reboot is needed) that gives the user a good amount of time to decide when to reboot.
With this in place you can push out updates and applications silently and then just sit back and wait for the users to reboot when they want to.
So, this is what you need to do…
Copy the following files to %ProgramFiles%RebootIfNeeded
- RebootIfNeeded.ps1 – Copy script below
- hstart64.exe – Download from http://www.ntwind.com/software/hstart.html (needed to hide the scheduled task completely)
- ShutdownTool.exe – Download from http://blog.coretech.dk/kea/new-version-of-the-coretech-shutdown-tool/ (you can find the latest version in the comments)
Create a Scheduled Task that runs once or twice every day (I have it set at 08:00 and 13:00 every weekday), and on that task create an with the following configuration:
Program: %ProgramFiles%RebootIfNeededhstart64.exe
Arguments:
/NOCONSOLE /WAIT ""%SystemRoot%system32WindowsPowerShellv1.0powershell.exe" -NoLogo -NoProfile -NonInteractive -File "%ProgramFiles%RebootIfNeededRebootIfNeeded.ps1""
(Tip: If using GPP, set item level targeting to check that the PS1-file exist before creating the task)
The script will check for pending reboots and if the computer havn’t rebooted for XX days.
You can add support for more languages, just extend the hashtable $restartDescriptions
[CmdletBinding()] PARAM ( $maxBootAgeDays = 35, $restartTimeOut = (9 * 60), # 9 hours $restartMaxPostpone = (48 * 60), # 48 hours $restartDescriptions = @{ "en-US" = "Your computer needs to restart to receive the latest updates."; "sv-SE" = "Din dator behöver startas om för att fÄ de senaste uppdateringarna."; }, $defaultLanguage = "en-US" ) Function Get-PendingReboot { # Local HKLM $HKLM = [UInt32] "0x80000002" $wmiRegistry = [WMIClass] "\.rootdefault:StdRegProv" #Default $PendingReboot = $false # CBS - Reboot Required ? $RegSubKeysCBS = $wmiRegistry.EnumKey($HKLM,"SOFTWAREMicrosoftWindowsCurrentVersionComponent Based Servicing") if ($RegSubKeysCBS.sNames -contains "RebootPending") { Write-Verbose "Component Based Servicing have a reboot pending" $PendingReboot = $true } # Windows Update - Reboot Required? $RegistryWUAU = $wmiRegistry.EnumKey($HKLM,"SOFTWAREMicrosoftWindowsCurrentVersionWindowsUpdateAuto Update") if ($RegistryWUAU.sNames -contains "RebootRequired") { Write-Verbose "Windows Update have a reboot required" $PendingReboot = $true } ## Pending FileRenameOperations ? $RegSubKeySM = $wmiRegistry.GetMultiStringValue($HKLM,"SYSTEMCurrentControlSetControlSession Manager","PendingFileRenameOperations") If ($RegSubKeySM.sValue) { $RegSubKeySM.sValue | ForEach-Object { If ($_.Trim() -ne '') { Write-Verbose "Pending FileRename operation: $($_)" } } $PendingReboot = $true } # ConfigMgr - Pending reboot ? TRY { $CCMClientSDK = Invoke-WmiMethod -NameSpace "ROOTccmClientSDK" -Class "CCM_ClientUtilities" -Name "DetermineIfRebootPending" -ErrorAction Stop If ($CCMClientSDK.IsHardRebootPending -or $CCMClientSDK.RebootPending) { Write-Verbose "ConfigMgr have reboot pending" $PendingReboot = $true } } CATCH { Write-Verbose "Cant talk to ConfigMgr Agent" } Write-Verbose "Pending reboot: $($PendingReboot)" Return $PendingReboot } Function Check-OldBootAge { PARAM ( $maxAgeDays = 35 ) $BootTime = Get-WmiObject Win32_Operatingsystem [Int]$days = (New-TimeSpan -Start $boottime.ConvertToDateTime($boottime.LastBootUpTime) -End (Get-Date)).TotalDays if ($days -ge $maxAgeDays) { Write-Verbose "Boot age is $($days) days (more than $($maxBootAgeDays)), reboot required" Return $true } else { Write-Verbose "Boot age is $($days) days (less than $($maxBootAgeDays))" Return $false } Return $days } Function Get-UserLanguage { Return [String] ([System.Threading.Thread]::CurrentThread).CurrentUICulture.Name } # ------------------------------------------------------------------------------------------------------------ # Main script if ( (Get-WmiObject -Query "SELECT ProductType FROM Win32_OperatingSystem").ProductType -eq 1) { If ( (Get-Process "ShutdownTool" -ErrorAction SilentlyContinue) ) { Write-Host "Already running ShutdownTool" } else { If ((Check-OldBootAge -maxAgeDays $maxBootAgeDays) -or (Get-PendingReboot)) { Write-Host "Reboot is required, calling restart utility" $userLanguage = Get-UserLanguage Write-Verbose "Language: $($userLanguage)" # Find description $Description = $restartDescriptions[$userLanguage] if ($Description -eq $null) { $Description = $restartDescriptions[$defaultLanguage] } $timeOutSeconds = ($restartTimeOut*60) - 1 Write-Verbose "Restart timeout: $($timeOutSeconds) seconds" Write-Verbose "Max postpone: $($restartMaxPostpone) minutes" Write-Verbose "Description: $($Description)" If ((Test-Path ".ShutdownTool.exe") -eq $false) { Throw "Cant find ShutdownTool.exe" } else { Write-Verbose "Calling restart with ShutdownTool" .ShutdownTool.exe /g:$userLanguage /d:"$Description" /t:$timeOutSeconds /m:$restartMaxPostpone /r /c } # /g - Language # /d - description # /t - countdown in sec # /m - max postpone in min # /r - reboot instead of shutdown # /c - force & remove abort-btn } } } else { Write-Verbose "Not a client OS" } # Done!
You must be logged in to post a comment.