This directory contains scripts that detect an Azure Spot VM eviction and trigger a graceful forced OS shutdown before Azure terminates the machine.
| File | Purpose |
|---|---|
Notify-AzureVMEviction.ps1 |
Production monitor - polls IMDS every 2 s; shuts down on eviction |
Shutdown VM on eviction.xml |
Task Scheduler definition example for recurring monitor execution |
Azure Spot VMs receive a 30-second eviction notice via the Instance Metadata Service (IMDS) Scheduled Events API:
GET http://169.254.169.254/metadata/scheduledevents?api-version=2020-07-01
Headers: Metadata: true
When an eviction is imminent the API returns a Preempt event:
{
"DocumentIncarnation": 2,
"Events": [
{
"EventId": "...",
"EventType": "Preempt",
"ResourceType": "VirtualMachine",
"EventStatus": "Scheduled",
"NotBefore": "2026-04-15T12:00:00Z"
}
]
}Notify-AzureVMEviction.ps1 polls this endpoint every 2 seconds.
On detecting a Preempt event it:
- Logs activity and event payloads to a text log file.
- Calls
shutdown.exe /s /f /t 8– forces all apps to close and shuts Windows down after 8 seconds.
- Windows Server (runs on the Azure VM itself)
- PowerShell 5.1 or PowerShell 7+
- The VM must be an Azure Spot (or Standard) VM – IMDS is available on all Azure VMs
- The log directory (
C:\Logs\by default) is created automatically
| Parameter | Default | Description |
|---|---|---|
-MetadataEndpoint |
IMDS link-local URL | Override for testing |
-TranscriptOutputDirectory |
C:\Logs\ |
Directory for PowerShell transcript log files |
-PollIntervalSeconds |
2 |
IMDS poll frequency |
-ShutdownTimeoutSeconds |
8 |
Seconds before OS shutdown executes |
# Run directly (blocks until eviction or Ctrl+C)
.\Notify-AzureVMEviction.ps1
# Custom transcript output directory
.\Notify-AzureVMEviction.ps1 -TranscriptOutputDirectory 'D:\Logs\'The monitor should start automatically on the VM and run under SYSTEM.
The included file Shutdown VM on eviction.xml is configured to:
- Run every 5 minutes
- Run as
SYSTEMwith highest privileges - Launch
pwsh.exe - Execute
C:\Logs\Notify-AzureVMEviction.ps1
Run once in an elevated PowerShell session:
$taskName = 'Shutdown VM on eviction'
$xmlPath = '.\Shutdown VM on eviction.xml'
# Ensure the script path referenced in XML exists, or edit XML first.
# Default in XML: C:\Logs\Notify-AzureVMEviction.ps1
Register-ScheduledTask -TaskName $taskName -Xml (Get-Content $xmlPath -Raw) -ForceIf you prefer schtasks.exe:
schtasks /Create /TN "Shutdown VM on eviction" /XML "Shutdown VM on eviction.xml" /FRun once with Administrator privileges on the VM:
$scriptPath = 'C:\Scripts\Notify-AzureVMEviction.ps1'
# Copy script to a stable location first
Copy-Item .\Notify-AzureVMEviction.ps1 $scriptPath -Force
$action = New-ScheduledTaskAction `
-Execute 'pwsh.exe' `
-Argument "-NonInteractive -WindowStyle Hidden -File `"$scriptPath`""
# Trigger: run at system startup
$trigger = New-ScheduledTaskTrigger -AtStartup
# Run as SYSTEM, highest privileges
$principal = New-ScheduledTaskPrincipal `
-UserId 'NT AUTHORITY\SYSTEM' `
-LogonType ServiceAccount `
-RunLevel Highest
$settings = New-ScheduledTaskSettingsSet `
-ExecutionTimeLimit ([TimeSpan]::Zero) ` # no time limit
-RestartCount 5 `
-RestartInterval ([TimeSpan]::FromMinutes(1)) `
-StartWhenAvailable
Register-ScheduledTask `
-TaskName 'AzureSpotVmEvictionMonitor' `
-TaskPath '\Azure\' `
-Action $action `
-Trigger $trigger `
-Principal $principal `
-Settings $settings `
-Forceschtasks /Create /F /RU "SYSTEM" /RL HIGHEST /SC ONSTART /DELAY 0000:30 ^
/TN "\Azure\AzureSpotVmEvictionMonitor" ^
/TR "pwsh.exe -NonInteractive -WindowStyle Hidden -File \"C:\Scripts\Notify-AzureVMEviction.ps1\""Get-ScheduledTask -TaskName 'AzureSpotVmEvictionMonitor' | Select-Object TaskName, State
# Start manually to verify it runs
Start-ScheduledTask -TaskName '\Azure\AzureSpotVmEvictionMonitor'
# Check the transcript log (filename is generated by Start-Transcript)
Get-ChildItem 'C:\Logs\' | Sort-Object LastWriteTime -Descending | Select-Object -First 1 | Get-Content -Tail 20 -WaitUnregister-ScheduledTask -TaskName 'AzureSpotVmEvictionMonitor' -Confirm:$false