Worklet: Predictable Reboot Notifications for Windows

  • 25 September 2019
  • 34 replies
  • 1515 views

Userlevel 5
Badge

Hey Y’all!


Automox offers a fully customizable end-user notification feature that clearly indicates to users when Automox will be patching and potentially rebooting their systems. This feature also allows you to set custom deferral options empowering users to control when their device should be patched. This gives peace of mind to system admins that their environment will be patched without interfering with their users everyday use.


Although this is a great feature, Automox recognizes that it does not offer the predictability to users to when the actual reboot event will take place. We are currently working on improving our current notifications to give users better reboot predictability. Automox has gathered a lot of customer feedback aimed to make this feature enhancement as effective as possible. The great thing about this is it will be built into the product and be offered in the same fashion as the current end-user notifications.


So, what can you do until this feature is released? The majority of the feedback we received is that we are great about notifying the user to when patching begins, however, once patching is executed we do not let the user know exactly when the reboot will take place and allow them to defer the reboot.


This level of reboot predictability may not be a requirement for you. If it is? Worklets to the rescue!


The below Worklet will send end-user notifications to users through the form of Windows notifications. The notifications are fully customizable, these options include:



  • Custom notification messages for each notification

  • Frequency of notification messages sent

  • Custom reboot timing and notifying

  • Final reboot message forces user acknowledgement


Prerequisite:

The policy Reboot Notifications needs to be disabled. This is so the Worklet and policy end-user notifications do not conflict.


The Worklet remediation needs some customizing by the user. Each value needs to be set correctly in order for the notification messages to patch the timer values set, otherwise it may not reboot correctly.


There are a few values that you’ll need to set in the script. They are easily indicated with the “$” in front of each. Here is a list and what they do below. Pay attention to the naming, you’ll get a sense of how the Worklet will flow given each value you set. Also, if you need any assistance setting this up I would be happy to help, just reply to this post and I will be sure to help get this setup for you.


$rebootimer (ex: $rebootimer=5400) - This hardsets the time until the device reboots, in seconds.


$message1 (ex: $message1={your_message}) - Copy for the custom message in the first notification. Be sure to indicate the amount of time in minutes until the reboot. This was set previously in $rebootimer


$message2wait (ex: $message2wait=1800) - the amount of time elapsed until the second notification is sent, in seconds.


$message2 - Copy for the custom message in the second notification. Be sure to indicate the amount of time in minutes until the reboot. This was set previously in $rebootimer minus the time elapsed that was set in $message2wait. Indicate this in minutes (5400-1800=3600) which is 60 minutes.


$rebootimer2 (ex: $rebootimer=5400) - resets the reboot timer to the time indicated in the previous notification message ($message2). If you indicated 60 minutes, then set this time to 3600 seconds


And so on….


As you can see, you need to see the reboot time, the custom message, and the wait timer for the notification intervals. The logic needs to make sense when setting these so it flows with what the custom notification says and what the behavior of the reboot is. Below is the evaluation and remediation code for the Worklet. This will help you get a visual for one that is fully customized.


Evaluation:


#exit 0 if no reboot is needed
#exit 1 if reboot is needed

$sysInfo = New-Object -ComObject "Microsoft.Update.SystemInfo"
if($sysInfo.RebootRequired)
{
exit 1}
else
{
exit 0}

Remediation:


$sysInfo = New-Object -ComObject "Microsoft.Update.SystemInfo"
if($sysInfo.RebootRequired)
{
######################################################### Start Parameters - Editable #########################################################

#hardset the reboot countdown timer in seconds (e.g. 5400 for 90 minutes). After this time elapses reboot command is initiated. In this example it's 90 minutes
$rebootimer = 5400
#set the first notification message copy. Include the reboot time value set previously. In this example it's 90 minutes
$message1 = "Automox will automatically reboot your computer in 90 minutes. Please take time to save and close any open documents."
#Define the time to wait until the second notification message is sent in seconds (e.g. 1800 for 30 minutes).
$message2wait = 1800
#set the second notification message copy. In this example 60 minutes.
$message2 = "Automox will automatically reboot your computer in 60 minutes. Please take time to save and close any open documents."
#reset the reboot time to match the time indicated in the previous notification message copy (e.g. 3600 for 60 minutes)
$rebootimer2 = 3600
#Define the time to wait for the third notification message to launch in seconds (e.g. 1800 for 30 minutes). In this example it's 30 minutes
$message3wait = 1800
#set the third notification message copy. In this example 30 minutes.
$message3 = "Automox will automatically reboot your computer in 30 minutes. Please take time to save and close any open documents."
#reset the reboot time to match the time indicated in the previois notification message copy (e.g.1800 for 30 minutes)
$rebootimer3 = 1800
#Define the time to wait for the final notification message to launch in seconds (e.g. 900 for 15 minutes). This needs to be in sync with when the reboot is scheduled.
#In this example it will warn the users 15 minutes before the reboot, so we set it to 900 (15 minutes)
$message4wait = 900
#set the final notification copy. The time for reboot should match the amount of time until the reboot take place
$message4 = "Automox will automatically reboot your computer in 15 minutes. Please take time to save and close any open documents."
#reset the reboot time to match the time indicated in the previois notification message copy (e.g.300 for 15 minutes)
$rebootimer4 = 900

################################################################ End Parameters ################################################################


### Commands being run ###
shutdown /a
shutdown /r /t $rebootimer /d p:4:1 /c $message1
Start-Sleep -Seconds $message2wait
shutdown /a
shutdown /r /t $rebootimer2 /d p:4:1 /c $message2
Start-Sleep -Seconds $message3wait
shutdown /a
shutdown /r /t $rebootimer3 /d p:4:1 /c $message3
Start-Sleep -Seconds $message4wait
shutdown /a
shutdown /r /t $rebootimer4 /d p:4:1 /c $message4
}
{
exit 0}

In the above example, I am setting the reboot countdown at 90 minutes. It will then send 3 more notifications warning the user of the reboot within the 90 minute with.



  1. The first notification will be sent notifying the user that a reboot will occur in 90 minutes

    30 minutes elapses

  2. The second notification message is sent and notifies the user a reboot will occur in 60 minutes

    30 minutes elapses

  3. The third notification message is sent and notifies the user a reboot will occur in 30 minutes

    15 minutes elapses

  4. The forth notification message is sent and notifies the user a reboot will occur in 15 minutes


After that the user will get notified 2 more times automatically through Windows. This will force the user to confirm before they can keep using their desktop. This happens at 10 minutes and again at 5 minutes. Below is an example of what this looks like



After that, the system will reboot. This gives the user plenty of time to save off any work before the reboot occurs. Again, you can make this longer if you desire. I do recommend that is kept under 8 hours as the agent will remain scanning during this Worklet and will not run any further commands until the Worklet has finished.


Here is an example of what the normal windows notification looks like on the device in the Worklet example above:



That’s it! You are now all set the notify user before rebooting a device. If you need any assistance with setting this up please feel free to reach out to me!


34 replies

Userlevel 4
Badge

Would you be willing to share this with us?

Userlevel 4
Badge

Here you go. Feel freer to reach out if you need help


.RebootNotifications.pdf (133.8 KB)

Userlevel 4
Badge

impressive script, kudo’s! Just curious why base encode an image and not use another file attached to the worklet?

Userlevel 4
Badge

Just an old habit. 🙂 You can definitely use the method you mentioned - that’s probably easier 🙂

Userlevel 4
Badge

Hi everyone. I recently made a change to this worklet. It now installs 2 scheduled tasks.


Here is the new reboot powershell script:


Add-Type @'
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;

namespace PInvoke.Win32 {

public static class UserInput {

[DllImport("user32.dll", SetLastError=false)]
private static extern bool GetLastInputInfo(ref LASTINPUTINFO plii);

[StructLayout(LayoutKind.Sequential)]
private struct LASTINPUTINFO {
public uint cbSize;
public int dwTime;
}

public static DateTime LastInput {
get {
DateTime bootTime = DateTime.UtcNow.AddMilliseconds(-Environment.TickCount);
DateTime lastInput = bootTime.AddMilliseconds(LastInputTicks);
return lastInput;
}
}

public static TimeSpan IdleTime {
get {
return DateTime.UtcNow.Subtract(LastInput);
}
}

public static int LastInputTicks {
get {
LASTINPUTINFO lii = new LASTINPUTINFO();
lii.cbSize = (uint)Marshal.SizeOf(typeof(LASTINPUTINFO));
GetLastInputInfo(ref lii);
return lii.dwTime;
}
}
}
}
'@

Function IsAutomaticReboot
{
<#

Two conditions must be met for automatic reboot
1. The local time must be between $rebootStartTime and $rebootEndTime
2. There has not been any user input for at least $idleTime minutes

#>

$boolTimeConditionMet = $False
$minTime = Get-Date $rebootStartTime
$maxTime = Get-Date $rebootEndTime
$now = Get-Date
if ($minTime.TimeOfDay -le $now.TimeOfDay -and $maxTime.TimeOfDay -ge $now.TimeOfDay)
{
$boolTimeConditionMet = $True
}

$boolIdleConditionMet = $False
$idleTime = [PInvoke.Win32.UserInput]::IdleTime
if ($idleTime.Minutes -ge $minIdleTime)
{
$boolIdleConditionMet = $True
}

return ($boolIdleConditionMet -and $boolTimeConditionMet)
}

Function RebootComputer
{
Restart-Computer -Force
}

#---------------------------------------------------------------------------------------
# These parameters control the time of day window within which automatic reboot will be initiated
[string]$rebootStartTime = "1am"
[string]$rebootEndTime = "5am"

# If there is no user input detected for $minIdleTime minutes within $rebootStarTime and $rebootEndTime, automatic reboot
# will be initiated
[int]$minIdleTime = 30

# Check if a reboot is pending
$sysInfo = New-Object -ComObject "Microsoft.Update.SystemInfo"
if($sysInfo.RebootRequired)
{
if (IsAutomaticReboot)
{
RebootComputer
}
}

This scheduled job needs to be installed with certain properties for it to work. And here is the powershell script to do this:


$action = New-ScheduledTaskAction -Execute wscript.exe -Argument "//B C:\ProgramData\amagent\rebootNotification\Hidden.vbs C:\ProgramData\amagent\rebootNotification\RunToastHidden.cmd" 
$trigger = New-ScheduledTaskTrigger -Daily -At 9am
$principal = New-ScheduledTaskPrincipal -UserID "NT AUTHORITY\SYSTEM" -LogonType ServiceAccount -RunLevel Highest
$settings = New-ScheduledTaskSettingsSet -MultipleInstances Parallel
$task = Register-ScheduledTask -TaskName "Reboot No Reminder" -Trigger $trigger -Action $action -User "NT AUTHORITY\SYSTEM"
$task.Triggers.Repetition.Interval = "PT1H"
$task.Triggers.Repetition.Duration = "P1D"
$task.Settings.StartWhenAvailable = $True
$task | Set-ScheduledTask -ErrorAction SilentlyContinue
Userlevel 4
Badge

The reason for the change is this: the previous script would not reboot the computer between 1am and 5am if there is no user logged on.

would it be possible to share this in a non-PDF output?

Maybe implement it directly in Automox ?

Userlevel 1
Badge

I think it would be helpful to include all dependencies, like this will require Toast notifications to be enabled or the user will never see these alerts (which is kinda a big deal).  You’d want to build some checks at the head of the script like the following, to a) check if notifications are enabled, and b) enable them.

function Test-WindowsPushNotificationsEnabled() {
$ToastEnabledKey = (Get-ItemProperty "HKCU:\Software\Microsoft\Windows\CurrentVersion\PushNotifications" -Name ToastEnabled -ErrorAction Ignore).ToastEnabled
if ($ToastEnabledKey -eq "1") {
Write-Host "Toast notifications for the logged on user are enabled in Windows"
$true
}
elseif ($ToastEnabledKey -eq "0") {
Write-Host "Toast notifications for the logged on user are not enabled in Windows. The script will try to enable toast notifications for the logged on user"
$false
}
}

# Create Enable-WindowsPushNotifications
# This is used to re-enable toast notifications if the user disabled them generally in Windows
function Enable-WindowsPushNotifications() {
$ToastEnabledKeyPath = "HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\PushNotifications"
Write-Host "Trying to enable toast notifications for the logged on user"
try {
Set-ItemProperty -Path $ToastEnabledKeyPath -Name ToastEnabled -Value 1 -Force
Get-Service -Name WpnUserService** | Restart-Service -Force
Write-Host "Successfully enabled toast notifications for the logged on user"
}
catch {
Write-Host "Failed to enable toast notifications for the logged on user. Toast notifications will probably not be displayed"
}
}

 

Reply