Scheduled Tasks
Note
Create, inspect, and manage Windows Scheduled Tasks entirely from PowerShell using the ScheduledTasks module — no Task Scheduler GUI required.
Overview
The ScheduledTasks module (built into Windows 8/Server 2012 and later) lets you define a task's action, trigger, and settings as PowerShell objects, then register it in one call. This replaces schtasks.exe for anything beyond the simplest one-liners, and it's the natural way to have a script deploy its own recurring schedule.
Basic Syntax
Get-ScheduledTask -TaskName "MyBackupTask"
$action = New-ScheduledTaskAction -Execute "powershell.exe" -Argument "-File C:\Scripts\backup.ps1"
$trigger = New-ScheduledTaskTrigger -Daily -At "2:00 AM"
Register-ScheduledTask -TaskName "MyBackupTask" -Action $action -Trigger $trigger
Start-ScheduledTask -TaskName "MyBackupTask"
Unregister-ScheduledTask -TaskName "MyBackupTask"
Key Points
- A task is built from three pieces: an Action (what to run), a Trigger (when), and optional Settings
Register-ScheduledTaskrequires an elevated session for tasks that run as SYSTEM or another user-Argumentfor a PowerShell script needs-File(or-Command) just like running powershell.exe manually- Task names support folders:
"MyGroup\MyBackupTask"organizes tasks in Task Scheduler's tree view
Viewing Existing Tasks
# All scheduled tasks
Get-ScheduledTask
# A specific task
Get-ScheduledTask -TaskName "MyBackupTask"
# Only your own tasks (not built-in Microsoft ones)
Get-ScheduledTask | Where-Object { $_.TaskPath -notlike "\Microsoft\*" }
# Task state and last run info
Get-ScheduledTask -TaskName "MyBackupTask" | Get-ScheduledTaskInfo
Output (Get-ScheduledTaskInfo):
LastRunTime : 7/1/2026 2:00:03 AM
LastTaskResult : 0
NextRunTime : 7/2/2026 2:00:00 AM
NumberOfMissedRuns : 0
TaskName : MyBackupTask
LastTaskResult 0 Means Success
A LastTaskResult of 0 means the task's action exited cleanly. Any non-zero value is an error code from whatever ran — check the action's own logging (or the task's history in Task Scheduler) to see what actually happened.
Creating a Scheduled Task
Building the Pieces
# Action: what actually runs
$action = New-ScheduledTaskAction `
-Execute "powershell.exe" `
-Argument "-NoProfile -ExecutionPolicy Bypass -File C:\Scripts\backup.ps1"
# Trigger: when it runs
$trigger = New-ScheduledTaskTrigger -Daily -At "2:00 AM"
# Settings: how it behaves
$settings = New-ScheduledTaskSettingsSet -StartWhenAvailable -DontStopOnIdleEnd
# Principal: which account it runs as
$principal = New-ScheduledTaskPrincipal -UserId "SYSTEM" -LogonType ServiceAccount -RunLevel Highest
Registering It
Register-ScheduledTask -TaskName "NightlyBackup" `
-Action $action `
-Trigger $trigger `
-Settings $settings `
-Principal $principal `
-Description "Runs the nightly backup script"
Common Trigger Types
# Daily at a specific time
New-ScheduledTaskTrigger -Daily -At "2:00 AM"
# Weekly on specific days
New-ScheduledTaskTrigger -Weekly -DaysOfWeek Monday, Wednesday, Friday -At "6:00 AM"
# One-time, at a specific date/time
New-ScheduledTaskTrigger -Once -At (Get-Date "2026-07-15 09:00")
# At system startup
New-ScheduledTaskTrigger -AtStartup
# At user logon
New-ScheduledTaskTrigger -AtLogOn
# Repeating: run every 30 minutes, indefinitely
New-ScheduledTaskTrigger -Once -At (Get-Date) -RepetitionInterval (New-TimeSpan -Minutes 30) -RepetitionDuration ([TimeSpan]::MaxValue)
Managing Existing Tasks
# Run a task immediately, outside its normal schedule
Start-ScheduledTask -TaskName "NightlyBackup"
# Stop a currently running task
Stop-ScheduledTask -TaskName "NightlyBackup"
# Disable without deleting
Disable-ScheduledTask -TaskName "NightlyBackup"
# Re-enable
Enable-ScheduledTask -TaskName "NightlyBackup"
# Remove entirely
Unregister-ScheduledTask -TaskName "NightlyBackup" -Confirm:$false
Updating a Task's Trigger
# Change when an existing task runs
$task = Get-ScheduledTask -TaskName "NightlyBackup"
$task.Triggers[0].StartBoundary = (Get-Date "2026-07-01 03:00").ToString("s")
Set-ScheduledTask -InputObject $task
Important Parameters
| Parameter | Type | Description | Example |
|---|---|---|---|
-TaskName | String | Task identifier | Get-ScheduledTask -TaskName "X" |
-TaskPath | String | Task Scheduler folder path | -TaskPath "\MyGroup\" |
-Execute | String | New-ScheduledTaskAction: program to run | -Execute "powershell.exe" |
-Argument | String | New-ScheduledTaskAction: arguments passed | -Argument "-File script.ps1" |
-Daily / -Weekly / -Once | Switch | New-ScheduledTaskTrigger: recurrence type | -Daily -At "2:00 AM" |
-UserId | String | New-ScheduledTaskPrincipal: run-as account | -UserId "SYSTEM" |
-RunLevel | String | Highest for elevated, Limited otherwise | -RunLevel Highest |
Common Patterns
# Pattern 1: Create-or-update — safe to re-run during deployment
$taskName = "NightlyBackup"
if (Get-ScheduledTask -TaskName $taskName -ErrorAction SilentlyContinue) {
Unregister-ScheduledTask -TaskName $taskName -Confirm:$false
}
Register-ScheduledTask -TaskName $taskName -Action $action -Trigger $trigger -Principal $principal
# Pattern 2: Report on task health across the system
Get-ScheduledTask | Where-Object { $_.State -eq "Ready" } | ForEach-Object {
$info = $_ | Get-ScheduledTaskInfo
[PSCustomObject]@{
Task = $_.TaskName
LastResult = $info.LastTaskResult
LastRun = $info.LastRunTime
NextRun = $info.NextRunTime
}
} | Where-Object { $_.LastResult -ne 0 }
# Pattern 3: Run a task and wait for it to finish
Start-ScheduledTask -TaskName "NightlyBackup"
do {
Start-Sleep -Seconds 5
$state = (Get-ScheduledTask -TaskName "NightlyBackup").State
} while ($state -eq "Running")
Real-World Examples
Example: Self-Deploying Scheduled Script
Scenario: A deployment script that installs itself as a scheduled task, replacing any prior version of the task.
$taskName = "DailyHealthCheck"
$scriptPath = "C:\Scripts\health-check.ps1"
# Remove old registration if it exists
if (Get-ScheduledTask -TaskName $taskName -ErrorAction SilentlyContinue) {
Unregister-ScheduledTask -TaskName $taskName -Confirm:$false
}
$action = New-ScheduledTaskAction -Execute "powershell.exe" `
-Argument "-NoProfile -ExecutionPolicy Bypass -File `"$scriptPath`""
$trigger = New-ScheduledTaskTrigger -Daily -At "6:00 AM"
$principal = New-ScheduledTaskPrincipal -UserId "SYSTEM" -LogonType ServiceAccount -RunLevel Highest
$settings = New-ScheduledTaskSettingsSet -StartWhenAvailable
Register-ScheduledTask -TaskName $taskName -Action $action -Trigger $trigger `
-Principal $principal -Settings $settings -Description "Runs daily health check"
Write-Output "Registered '$taskName' to run daily at 6:00 AM"
Explanation: Unregistering before re-registering makes the script idempotent — running it twice doesn't create duplicate tasks or leave a stale trigger behind. Running as SYSTEM avoids the task failing later because a user account got locked or its password expired.
Tips & Tricks
Quote Paths With Spaces in -Argument
# BAD - breaks if the path has spaces
-Argument "-File C:\Program Files\Scripts\backup.ps1"
# GOOD - escaped quotes around the path
-Argument "-File `"C:\Program Files\Scripts\backup.ps1`""
-Argument string is passed to powershell.exe as a single command line — spaces in paths need their own quoting, separate from the outer PowerShell string quoting. SYSTEM Tasks Can't Access Mapped Network Drives
A task running as SYSTEM has no access to the interactively-logged-in user's mapped drives or credentials. Use full UNC paths (\\server\share\...) instead of drive letters, and store any needed credentials with Register-ScheduledTask -User -Password or a credential vault rather than relying on the user's session.
Related Topics
- Services Management - An alternative for tasks that need to run continuously rather than on a schedule
- Event Logs - Task Scheduler logs execution history to Microsoft-Windows-TaskScheduler/Operational
- Script Configuration Files - Externalizing settings for a scheduled script