Get an email when Windows reboots itself
Some machines just have to stay logged in. An interface PC that runs a vendor client, a box that hosts a POS bridge, a small on-prem gateway – you know the type. Windows Update reboots it at 3 a.m., the session is gone, and the first you hear about it is when someone calls because "the thing doesn't work".
I wanted something dead simple: if the machine reboots, I get an email. No agent, no monitoring suite, no cloud subscription. Just Task Scheduler and a bit of PowerShell.
The idea
- A scheduled task with the trigger At startup, running as
SYSTEMso it fires before anyone logs in - A PowerShell script that grabs the last shutdown reason from the event log and sends a mail via plain SMTP
- A dedicated mailbox on a subdomain, so nothing touches the tenant's main mail flow
Why a subdomain mailbox
My main domain lives on Microsoft 365. Basic auth for SMTP client submission is gone, and I didn't want to fiddle with app registrations or Graph just to send a one-liner from an unattended box.
So I created a mailbox like alerts@notify.example.com at my web hoster. The mailbox itself is free with the hosting package; all it needs is a handful of DNS records on the subdomain – not on the root domain, that stays untouched on M365:
Host |
Type |
Value |
|---|---|---|
notify |
MX |
the hoster's MX servers |
notify |
TXT |
|
SPF does not inherit from the parent domain, so the subdomain needs its own record. Without it, Exchange Online shows the yellow "we couldn't verify the sender" banner and may greylist the message.
Small trap: the hoster's setup wizard suggested adding its MX records to the root domain. Don't. That would split your tenant's mail flow.
The script
Save as C:\Scripts\RebootMail.ps1 (UTF-8 with BOM if you use umlauts in the body).
# --- Configuration ---
$SmtpServer = 'mail.your-hoster.example'
$SmtpPort = 587
$From = 'alerts@notify.example.com'
$User = 'alerts@notify.example.com'
$Password = 'REPLACE-ME'
$To = 'you@example.com' # array for multiple recipients
$LogFile = 'C:\Scripts\RebootMail.log' # --- Collect data ---
$boot = (Get-CimInstance Win32_OperatingSystem).LastBootUpTime
$reason = Get-WinEvent -FilterHashtable @{LogName='System'; Id=1074} -MaxEvents 1 -ErrorAction SilentlyContinue
$ip = (Get-NetIPAddress -AddressFamily IPv4 |
Where-Object { $_.IPAddress -notlike '127.*' -and $_.IPAddress -notlike '169.254.*' } |
Select-Object -First 1).IPAddress $reasonText = if ($reason) { "$($reason.TimeCreated)`n$($reason.Message)" }
else { 'No event 1074 found (power loss / crash?)' } $body = @"
$env:COMPUTERNAME has rebooted and is waiting at the logon screen. Boot time: $boot
IP: $ip Last shutdown reason (event 1074):
$reasonText
"@ # --- Send with retry (network may not be up yet) ---
$cred = New-Object System.Management.Automation.PSCredential(
$User, (ConvertTo-SecureString $Password -AsPlainText -Force)) $sent = $false
for ($i = 1; $i -le 5 -and -not $sent; $i++) {
try {
Send-MailMessage -From $From -To $To `
-Subject "Reboot: $env:COMPUTERNAME ($(Get-Date -Format 'yyyy-MM-dd HH:mm'))" `
-Body $body -SmtpServer $SmtpServer -Port $SmtpPort -UseSsl -Credential $cred -Encoding UTF8
"$(Get-Date) OK (attempt $i)" | Out-File $LogFile -Append
$sent = $true
}
catch {
"$(Get-Date) ERROR attempt $i : $($_.Exception.Message)" | Out-File $LogFile -Append
Start-Sleep -Seconds 60
}
}
Event ID 1074 is the part I like most. It tells you who rebooted the box and why:
Vom Prozess "C:\WINDOWS\servicing\TrustedInstaller.exe" ... aus folgendem Grund initiiert: "Betriebssystem: Aktualisierung (geplant)"
TrustedInstaller + "Aktualisierung (geplant)" = Windows Update. Explorer.EXE + a username = someone clicked Restart. Nice to know before you drive out.
Yes, Send-MailMessage is officially deprecated. It still ships, it still does STARTTLS on 587, and for an internal alert it is perfectly fine.
Setting it up
All in an elevated PowerShell.
Lock down the folder – the script contains a password in clear text:
icacls C:\Scripts /inheritance:r /grant:r "SYSTEM:(OI)(CI)F" "Administrators:(OI)(CI)F"
Test it manually first:
powershell -ExecutionPolicy Bypass -File C:\Scripts\RebootMail.ps1
Get-Content C:\Scripts\RebootMail.log
Register the task:
$action = New-ScheduledTaskAction -Execute 'powershell.exe' `
-Argument '-NoProfile -ExecutionPolicy Bypass -WindowStyle Hidden -File C:\Scripts\RebootMail.ps1'
$trigger = New-ScheduledTaskTrigger -AtStartup
$trigger.Delay = 'PT2M'
$settings = New-ScheduledTaskSettingsSet -StartWhenAvailable -ExecutionTimeLimit (New-TimeSpan -Minutes 10)
Register-ScheduledTask -TaskName 'Reboot-Mail' -Action $action -Trigger $trigger `
-Settings $settings -User 'SYSTEM' -RunLevel Highest
Then run it once as SYSTEM to make sure it works without your user context:
Start-ScheduledTask -TaskName 'Reboot-Mail'
Start-Sleep 15; Get-Content C:\Scripts\RebootMail.log
Finally, Restart-Computer. Mine arrived about 90 seconds after the boot – before I had even reached the logon screen.
Things I ran into
-
The 2-minute delay matters.
SYSTEMstarts early, DNS and the NIC may not be ready. The retry loop covers the rest. - "It only arrives after I log in." No, it doesn't – you're just reading it in Outlook on the same machine. Check your phone.
- SPF first, then test. The first mail without SPF went through with a warning banner; the second sat in greylisting for a while. After the TXT record propagated, delivery was instant.
- Task shows "Running" after it's done. The hidden PowerShell instance lingers until the execution time limit kills it. Cosmetic.
When not to do this
If the actual goal is "the machine should just be logged in again", use Sysinternals Autologon instead. It stores the credentials encrypted as an LSA secret and logs the user on automatically. The mail is for when you want to know – Autologon is for when you want it fixed.