PowerShell Scheduled Task: Create, Edit, and Audit via CIM

Configuring a scheduled task through the Task Scheduler GUI is fine for a single machine, but deploying the same task across 200 servers requires a repeatable, scriptable approach. PowerShell’s ScheduledTasks module — backed by CIM rather than the legacy COM object — gives you full task lifecycle management: create, modify, enable, disable, and audit. You can deploy a standard maintenance task fleet-wide in a single loop and confirm every machine has the correct configuration without opening a single remote desktop session.
Quick Answer
Use New-ScheduledTaskAction, New-ScheduledTaskTrigger, and New-ScheduledTaskPrincipal to build the task components, then register everything with Register-ScheduledTask. Audit existing tasks with Get-ScheduledTask and Get-ScheduledTaskInfo.
Creating a Task with New-ScheduledTaskAction and New-ScheduledTaskTrigger
An action defines what runs; a trigger defines when. Both are separate objects that get passed to Register-ScheduledTask. You can combine multiple triggers on a single task — for example, a daily schedule plus an at logon trigger.
# Define the action: run a PowerShell script
$action = New-ScheduledTaskAction `
-Execute "pwsh.exe" `
-Argument "-NonInteractive -ExecutionPolicy Bypass -File C:\Scripts\Maintenance.ps1" `
-WorkingDirectory "C:\Scripts"
# Define the trigger: daily at 03:00 AM
$trigger = New-ScheduledTaskTrigger -Daily -At "03:00"
# Define optional settings
$settings = New-ScheduledTaskSettingsSet `
-ExecutionTimeLimit (New-TimeSpan -Hours 1) `
-RestartCount 2 `
-RestartInterval (New-TimeSpan -Minutes 5) `
-StartWhenAvailable
Always set -WorkingDirectory in the action. Without it the task starts in C:\Windows\System32, which causes relative path references inside the script to fail silently.
Setting RunAs Account with New-ScheduledTaskPrincipal
The principal controls which account runs the task and at what privilege level. For tasks that need network access use a domain service account or group-managed service account (gMSA). For tasks that need local administrator rights set -RunLevel Highest.
# Run as a specific service account with highest privilege
$principal = New-ScheduledTaskPrincipal `
-UserId "CORP\svc-maintenance" `
-LogonType Password `
-RunLevel Highest
# Alternatively, run as SYSTEM for local-only tasks
$systemPrincipal = New-ScheduledTaskPrincipal `
-UserId "NT AUTHORITY\SYSTEM" `
-LogonType ServiceAccount `
-RunLevel Highest
Registering and Enabling with Register-ScheduledTask
Bring all the components together with Register-ScheduledTask. Use -Force to overwrite an existing task with the same name — this is the idempotent deployment pattern for fleet management.
Register-ScheduledTask `
-TaskName "Corp-NightlyMaintenance" `
-TaskPath "\Corporate\" `
-Action $action `
-Trigger $trigger `
-Principal $principal `
-Settings $settings `
-Description "Nightly system maintenance — deployed via PowerShell" `
-Force
# Confirm it registered successfully
Get-ScheduledTask -TaskName "Corp-NightlyMaintenance" -TaskPath "\Corporate\" |
Select-Object TaskName, State, TaskPath
Modifying Existing Tasks Without Deleting Them
Use Set-ScheduledTask to update individual components of an existing task. You only need to pass the components that are changing — the others remain untouched. This is important for tasks that are currently in a Running state, as it avoids interrupting an active job.
# Update only the trigger to change the execution time
$newTrigger = New-ScheduledTaskTrigger -Daily -At "02:00"
Set-ScheduledTask `
-TaskName "Corp-NightlyMaintenance" `
-TaskPath "\Corporate\" `
-Trigger $newTrigger
Auditing All Tasks on Remote Servers with Get-ScheduledTask via CIM
The ScheduledTasks module uses CIM internally. Pass -CimSession objects to run the audit against remote machines without using Invoke-Command.
$servers = "srv-app01", "srv-app02", "srv-db01"
$sessions = New-CimSession -ComputerName $servers
$allTasks = Get-ScheduledTask -CimSession $sessions |
Get-ScheduledTaskInfo |
Select-Object TaskName, TaskPath, LastRunTime, LastTaskResult,
NextRunTime, PSComputerName
$allTasks | Where-Object { $_.LastTaskResult -ne 0 } |
Format-Table TaskName, PSComputerName, LastTaskResult -AutoSize
Remove-CimSession $sessions
LastTaskResult of 0 means success; any other value is an error code from the task’s last execution.
Finding Tasks Running as Sensitive Service Accounts
Security audits often require identifying scheduled tasks that run as privileged service accounts. Query across all servers and filter on the Principal property to build this inventory.
Common Errors
- Task runs but immediately exits: The most common cause is a missing
-WorkingDirectoryon the action, causing the script to fail to find its dependencies. Always specify a fully qualified-WorkingDirectoryand use absolute paths inside the script itself. - Highest privilege flag required for UAC elevation: If the task needs to perform operations that require elevation (writing to
HKLM, modifying system files), set-RunLevel HighestinNew-ScheduledTaskPrincipal. Without it, the task runs in a restricted token even if the account is an administrator.
Related Cmdlets / See Also
Wrapping Up
Managing scheduled tasks entirely in PowerShell makes fleet-wide deployments repeatable and auditable. Build your task components once, register with -Force for idempotency, and use CIM sessions to audit task health across all servers from a single management script.


