Restart a Windows device on a recurring schedule, warning the signed-in user first, or cancel a pending restart
Restart a Windows device on a recurring schedule, such as every 7 days, with a delay and a message that tells signed-in users when and why, or cancel a pending restart. For MSPs and IT teams with terminal servers, kiosks and line-of-business servers that need regular restarts.
| Runs in | On the device (PowerShell, as SYSTEM) |
| Uses | None |
| Suggested approval | None, so only Engineers and Admins can run it, for workstations and kiosks. For servers, a copy that needs an admin's approval, set before you create the schedule. Never Anyone |
| Category | Devices |
Test it with SuperIT before you rely on it
This is an example, not a ready-made action, and it hasn't been tested end to end. Before you use it, work through it with SuperIT: ask SuperIT to review the script against your environment and requirements, and adjust it together. Then run it on a test device, as in Test it. Device actions can't use Test, so every run is real.
Why use an action instead of asking SuperIT
SuperIT can't repeat a chat command, and creating a scheduled task on the device is denied by the default command policies. An action can run on a schedule, so a terminal server restarts every Sunday at 2am with the same delay and warning each time. It runs as SYSTEM and isn't checked against command policies; its authorization policy decides who can run it.
For a one-off "restart this PC at lunchtime", just ask SuperIT in chat: it can run shutdown with a delay and a message after one approval, and cancel it.
When to use it
- "Restart the terminal server SRV-RDS01 every Sunday at 2am."
- "The reception kiosk needs a nightly restart."
- "SRV-APP02 should restart weekly, a few hours after its scheduled updates."
- "Cancel tonight's restart on SRV-RDS01, the finance team is working late."
What the action does
- Reads
confirm_hostname,delay_minutes(1 to 1440, default 15), the optionalmessage, and thecancelswitch. - Stops without doing anything unless
confirm_hostnameis this device's computer name. This is your confirmation that you mean to restart this device, and it stops a run that lands on the wrong one. - If
cancelis on, cancels a pending restart withshutdown /aand stops. - Otherwise, reports who is signed in at the console, then runs
shutdown /r /t <seconds> /c "<message>" /d p:0:0. - Windows shows the signed-in user a notice that the device will restart, with your message.
- Prints the restart time and returns it as a result.
Unsaved work is lost
When the delay runs out, Windows closes apps without asking, so unsaved work is lost. The message is the user's only warning, so give them enough time and say what to do.
What it doesn't do:
- It doesn't let the user postpone the restart. They can only save their work.
- It doesn't restart at a clock time by itself. The schedule sets when it starts, and
delay_minutesis the warning period after that. - It doesn't wait for or check that the device came back. Check the device's status in SuperIT afterwards.
Before you begin
- A Windows device with the SuperIT RMM agent, online. Actions run on Windows devices only; the macOS and Linux agents are in Beta and can't run actions. See Requirements.
- Your role. Creating an action in a chat needs the Admin role. Engineers and Admins can build it under Workflows → Actions.
- No credential. See Credentials.
- An authorization policy, decided before you schedule it. With None, only Engineers and Admins can run it, without approval. Never add an Anyone rule: SuperIT doesn't check that a device belongs to the person who asked. A scheduled run uses the current policy, so if it needs approval every run waits for an approver, and expires after 7 days if nobody approves it. See Authorization policies and approvals.
Build it with SuperIT
Create an action called "Schedule a reboot with a warning".
Runtime: PowerShell, running on the device as SYSTEM (execution target: device).
Inputs: confirm_hostname (string, required), delay_minutes (string, required, default 15, 1-1440), message (string, optional), cancel (boolean, optional).
Stop before doing anything unless confirm_hostname matches $env:COMPUTERNAME.
If cancel is true: shutdown /a; treat exit 1116 as "nothing to cancel".
Else: default message "IT support will restart this computer in N minutes to finish maintenance. Please save your work now."; replace double quotes, strip trailing backslashes; max 500 characters. Print the console user (Win32_ComputerSystem.UserName). Run shutdown /r /t <seconds> /c <message> /d p:0:0; exit 1190 means a restart is already scheduled.
Print the restart time. Windows PowerShell 5.1 compatible. No authorization policy.
SuperIT confirms the brief, drafts the script, validates it and creates the action as a draft. Review the script, then publish it under Workflows → Actions.
Or build it yourself
Create the action under Workflows → Actions with the PowerShell runtime and these inputs, then set it to run on a device.
| Name | Type | Required | Example |
|---|---|---|---|
confirm_hostname |
string | Yes | SRV-RDS01 |
delay_minutes |
string | Yes | 15 |
message |
textarea | No | Restarting to finish this month's updates. Please save your work. |
cancel |
boolean | No | false |
On a device, the script runs in Windows PowerShell as SYSTEM with $ErrorActionPreference = "Stop" already set. Inputs arrive as strings in $ctx.inputs; booleans are "true" or "false". Invoke-RebootSchedule at the top lists the steps in order; each step is a small function below it, and the last line runs it. $result is set as $script:result, because a plain $result = inside a function stays local and never reaches the Result tab.
# Schedules a restart in N minutes with a warning shown to signed-in users, or cancels a pending
# one, and prints the restart time.
function Invoke-RebootSchedule {
param($ConfirmHostname, $DelayMinutes, $Message, $Cancel)
Assert-TargetDevice -ConfirmHostname $ConfirmHostname
Write-Output '=== SCHEDULED RESTART ==='
if ($Cancel -eq 'true') {
Undo-ScheduledRestart
return
}
$minutes = Assert-DelayMinutes -Value $DelayMinutes
$text = Get-WarningMessage -Message $Message -Minutes $minutes
$user = (Get-CimInstance Win32_ComputerSystem).UserName
if ($user) { Write-Output "Signed in at the console: $user" } else { Write-Output "Nobody is signed in at the console." }
Request-Restart -Minutes $minutes -Message $text
$at = (Get-Date).AddMinutes($minutes)
Write-Output ("{0} will restart at {1:yyyy-MM-dd HH:mm} (in {2} minutes)." -f $env:COMPUTERNAME, $at, $minutes)
Write-Output "Message shown: $text"
# $result is read after the script ends, so set it at script scope, not in this function.
$script:result = @{ restart_at = $at.ToString('o'); minutes = $minutes; console_user = $user; message = $text }
}
function Assert-TargetDevice {
param($ConfirmHostname)
$expected = ("$ConfirmHostname".Trim() -split '\.')[0]
if (-not $expected) { throw "confirm_hostname is required: enter this device's computer name to confirm the restart." }
if ($expected -ne $env:COMPUTERNAME) { throw "This run reached '$env:COMPUTERNAME', not '$expected'. Nothing was scheduled or cancelled." }
}
function Invoke-Shutdown([string[]]$Arguments) {
# shutdown.exe reports "nothing to cancel" on stderr; under "Stop" that would end the script.
$ErrorActionPreference = 'Continue'
$out = (& shutdown.exe @Arguments 2>&1 | Out-String).Trim()
@{ code = $LASTEXITCODE; output = $out }
}
function Undo-ScheduledRestart {
$r = Invoke-Shutdown @('/a')
if ($r.code -eq 0) { Write-Output "Cancelled the scheduled restart on $env:COMPUTERNAME." }
elseif ($r.code -eq 1116) { Write-Output "No restart was scheduled on $env:COMPUTERNAME." }
else { throw "shutdown /a failed (exit $($r.code)): $($r.output)" }
$script:result = @{ cancelled = ($r.code -eq 0) }
}
function Assert-DelayMinutes {
param($Value)
$delay = "$Value".Trim()
if ($delay -notmatch '^\d{1,4}$' -or [int]$delay -lt 1 -or [int]$delay -gt 1440) { throw "delay_minutes must be a whole number from 1 to 1440." }
[int]$delay
}
function Get-WarningMessage {
param($Message, [int]$Minutes)
$text = "$Message".Trim()
if (-not $text) { $text = "IT support will restart this computer in $Minutes minutes to finish maintenance. Please save your work now." }
# A double quote would end the /c argument early.
$text = $text -replace '"', "'" -replace '\s+', ' '
if ($text.Length -gt 500) { $text = $text.Substring(0, 500) }
# Windows PowerShell 5.1 quotes the argument, and a trailing backslash would escape the closing quote.
$text.TrimEnd('\')
}
function Request-Restart {
param([int]$Minutes, [string]$Message)
$r = Invoke-Shutdown @('/r', '/t', ($Minutes * 60), '/c', $Message, '/d', 'p:0:0')
if ($r.code -eq 1190) { throw "A restart or shutdown is already scheduled on $env:COMPUTERNAME. Cancel it first." }
if ($r.code -ne 0) { throw "shutdown failed (exit $($r.code)): $($r.output)" }
}
Invoke-RebootSchedule -ConfirmHostname $ctx.inputs.confirm_hostname -DelayMinutes $ctx.inputs.delay_minutes `
-Message $ctx.inputs.message -Cancel $ctx.inputs.cancel
Users signed in through Remote Desktop also see the notice. The console user line reports only the user at the physical screen. Assert-TargetDevice compares the first part of confirm_hostname with $env:COMPUTERNAME, so SRV-RDS01 and SRV-RDS01.contoso.local both match.
Test it
You can't use Test on an action that runs on a device. (On a cloud-sandbox action, Test isn't a dry run either: it runs the script for real and skips approvals.) Every device run really schedules a restart, so use a test device.
- Publish the action under Workflows → Actions.
- Sign in to the test device, then select Run on the action, enter the test device's computer name in
confirm_hostname, setdelay_minutesto10, pick that device under Run target, and select Run action. - Check that the notice appears on the device and Stdout shows the restart time.
- Run it again with
cancelon and confirm the restart is cancelled. - Run it with a different name in
confirm_hostnameand confirm it fails with "Nothing was scheduled or cancelled".
Use it
- On a schedule. Ask SuperIT "Run Schedule a reboot with a warning on SRV-RDS01 every 7 days, starting Sunday at 2am, with a 10-minute delay and confirm_hostname SRV-RDS01". A schedule is tied to one device, so create one per device.
- After scheduled updates. Schedule it a few hours after Check and install Windows updates on the same device, so the install has finished.
- To cancel one restart. Ask SuperIT "Run Schedule a reboot with a warning on SRV-RDS01 with cancel on and confirm_hostname SRV-RDS01" during the delay. To stop the restarts for good, ask SuperIT to pause or cancel the schedule.
- Not from tickets automatically. Don't switch on Run automatically for this action. A run started from a ticket's Recommended actions panel goes to the action's default device or your team's designated device.
- For clients. MSPs can publish it to managed teams.
Always pick the device. If no device is chosen, SuperIT runs a device action on the action's default device or your team's designated device, and confirm_hostname stops the run if that's the wrong one.
Variations
- Restart only if needed: check the pending-restart registry keys
HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update\RebootRequiredand...\Component Based Servicing\RebootPendingfirst, and stop if neither exists. - Skip the restart if a user is signed in, for servers or kiosks that must restart only when idle.
- Send an extra message to every session with
msg.exe * "<text>"before scheduling.msg.exeisn't available on Windows Home.