When migrating from Windows Server 2012 to 2016, many administrators encounter this frustrating behavior: scheduled tasks with start times in the past simply refuse to execute. Unlike Server 2012 which would honor the recurrence pattern regardless of initial start time, Server 2016 requires the first run to be in the future.
Consider this common monitoring scenario:
$Action = New-ScheduledTaskAction -Execute "Powershell.exe" -Argument "C:\Monitoring\check_services.ps1"
$Trigger = New-ScheduledTaskTrigger -Once -At (Get-Date).Date -RepetitionInterval (New-TimeSpan -Minutes 5)
Register-ScheduledTask -TaskName "ServiceMonitor" -Action $Action -Trigger $Trigger
This works perfectly in 2012 but fails silently in 2016 after server reboots.
The most reliable solution is to create a one-time trigger that runs indefinitely:
$Trigger = New-ScheduledTaskTrigger -Once -At (Get-Date).AddMinutes(5) -RepetitionInterval (New-TimeSpan -Minutes 5)
$Settings = New-ScheduledTaskSettingsSet -StartWhenAvailable -DontStopOnIdleEnd
Register-ScheduledTask -TaskName "ServiceMonitor" -Action $Action -Trigger $Trigger -Settings $Settings
For non-regular intervals, consider these approaches:
- Task Wrapper Script:
# check_last_run.ps1 $lastRun = Get-Date -Date (Get-ScheduledTaskInfo -TaskName "MyTask").LastRunTime if ((New-TimeSpan -Start $lastRun -End (Get-Date)).TotalMinutes -ge 30) { & "C:\Path\To\ActualTask.ps1" }
- Trigger Combination:
$Trigger1 = New-ScheduledTaskTrigger -AtStartup $Trigger2 = New-ScheduledTaskTrigger -Once -At (Get-Date).AddMinutes(5) -RepetitionInterval (New-TimeSpan -Minutes 5) Register-ScheduledTask -TaskName "HybridTask" -Action $Action -Trigger @($Trigger1,$Trigger2)
Microsoft has acknowledged this behavior in KB articles, though no official fix exists. Some administrators have had success with:
reg add "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Schedule\Configuration" /v "AllowPastStartTime" /t REG_DWORD /d 1 /f
Note: This undocumented setting may not work in all environments.
After implementing any solution, verify with:
Get-ScheduledTask | Where-Object {$_.State -ne "Ready"} | Format-Table -AutoSize
Get-ScheduledTaskInfo -TaskName "YourTask" | Select-Object LastRunTime,NextRunTime,LastTaskResult
When migrating scheduled tasks from Windows Server 2012 R2 to 2016, many administrators encounter a critical behavioral change: tasks configured with start times in the past simply won't execute. This creates particular challenges for:
- Recurring monitoring tasks (e.g., every 3-5 minutes)
- System maintenance scripts
- Batch processing jobs
In Server 2012 R2, you could create a task like this (PowerShell example):
$Action = New-ScheduledTaskAction -Execute 'powershell.exe' -Argument '-File C:\monitoring\check_services.ps1'
$Trigger = New-ScheduledTaskTrigger -Daily -At "12:00AM" -RepetitionInterval (New-TimeSpan -Minutes 5)
Register-ScheduledTask -TaskName "ServiceMonitor" -Action $Action -Trigger $Trigger
This would immediately begin executing every 5 minutes. Server 2016 ignores such configurations completely.
The most reliable solution we've found involves creating a "One Time" trigger with indefinite repetition:
$Trigger = New-ScheduledTaskTrigger -Once -At (Get-Date).AddMinutes(1) -RepetitionInterval (New-TimeSpan -Minutes 5)
Key advantages:
- Survives server reboots
- Doesn't require multiple triggers
- Maintains predictable execution intervals
For more complex scheduling needs, consider these approaches:
1. Wrapper Script Method
# File: task_wrapper.ps1
while ($true) {
& C:\scripts\actual_task.ps1
Start-Sleep -Seconds 300 # 5 minute interval
}
2. Event-Based Triggering
$Trigger = New-ScheduledTaskTrigger -AtStartup
$Settings = New-ScheduledTaskSettingsSet -StartWhenAvailable -DontStopOnIdleEnd
While not officially documented, some administrators report success with these registry modifications:
Windows Registry Editor Version 5.00
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Schedule\TaskCache\Tree]
"BypassPastStartTimeCheck"=dword:00000001
Use with caution and thoroughly test in non-production environments first.
Implement robust monitoring since failed tasks won't appear in task history:
# PowerShell monitoring script example
$Tasks = Get-ScheduledTask | Where-Object { $_.State -eq "Ready" -and $_.NextRunTime -lt (Get-Date).AddMinutes(-10) }
if ($Tasks) {
Send-MailMessage -To "admin@domain.com" -Subject "Stuck Tasks Alert" -Body ($Tasks | Out-String)
}