PowerShell script not running on startup

Summary

Production incident involving a PowerShell script designed to send reboot alerts failing to execute during Windows startup. Despite successful manual execution, automated startup methods (Task Scheduler, .bat files, startup folder) didn’t trigger the script, causing delayed incident notifications.

Root Cause

Two primary technical failures:

  • Contextual permission mismatch:
    • Task Scheduler executed the script under SYSTEM or a non-interactive user context lacking required privileges, whereas manual tests used interactive/admin sessions.
  • PowerShell execution policy enforcement:
    • Startup methods couldn’t bypass the PowerShell Restricted execution policy that prevented script execution.

Why This Happens in Real Systems

Three systemic patterns enable this failure:

  • Non-interactive sessions block script execution by default due to security policies.
  • Execution policies are scoped to users/processes, creating inconsistent behavior between manual/automated execution.
  • Silent failure modes exist when scripts lack explicit startup logging, masking errors.

Real-World Impact

  • Missed critical alerts during server reboots, delaying incident response.
  • False confidence from manual tests not reflecting runtime environment conditions.
  • Orphaned alerts due to missing startup dependencies (e.g., missing modules, network latency during boot).

Example or Code

Minimal Task Scheduler configuration to override execution policy and context:

# Executes script as LocalSystem with unrestricted permissions  
$Trigger = New-ScheduledTaskTrigger -AtStartup  
$Action = New-ScheduledTaskAction -Execute "Powershell.exe" -Argument `  
  "-NoProfile -ExecutionPolicy Bypass -File C:\Path\To\Script.ps1"  
Register-ScheduledTask "RebootAlert" -Action $Action -Trigger $Trigger -User "NT AUTHORITY\SYSTEM" -RunLevel Highest

How Senior Engineers Fix It

  1. Explicitly define execution context:
    • Validate and configure Task Scheduler to use NT AUTHORITY\SYSTEM or a privileged account with script-specific permissions.
  2. Override restrictive policies:
    • Use -ExecutionPolicy Bypass in PowerShell arguments to ignore safety locks.
  3. Add startup diagnostics:
    • Embed logging (e.g., Start-Transcript) to capture silent failures.
  4. Package dependencies:
    • Deploy scripts/modules to $env:ProgramFiles to avoid user-specific path issues.

Why Juniors Miss It

  • Assumption uniformity: Manual tests work → automated “should work,” ignoring context/security nuances.
  • Insufficient debugging: Not testing scheduled tasks via powershell.exe -Command "Get-ScheduledTask | gangsdebug" or checking Event Viewer logs.
  • Security policy gaps: Underestimating how PowerShell’s execution policy behaves across ], sessions and user accounts.
  • Overlooking failure signatures: Silent errors (e.g., 0x1 exit code) dismissed without deeper investigation.