Shore Up
a clockwork timer wired to a master key that opens every door in a building, with a small warning tag hanging off the key
Windows

Run Scheduled Tasks as SYSTEM With Highest Privileges

Ketan Aagja8 min read
No ratings yet

Before you run this

This guide creates a Windows scheduled task that runs a script non-interactively under NT AUTHORITY\SYSTEM with Run with highest privileges enabled. That combination gives the task the full rights of the local machine account and an unfiltered admin token — it is the most powerful context a local task can run in. Use it only for jobs that genuinely need it (patching, service restarts, disk maintenance), and keep the script it runs small and trusted, because anything that task does, it does with no guardrails.

  • Elevation required. Creating a SYSTEM task, or any task with -RunLevel Highest, requires an elevated (Run as administrator) PowerShell or command prompt. A normal user session cannot register a task that runs as SYSTEM. The built-in ScheduledTasks module ships with Windows 8 / Server 2012 and later, so there is nothing to install on Server 2022.
  • Test first. Read the script your task will call before you wire it up, and register the task on a test VM or a non-production box first. Run it once manually and confirm the outcome before you trust it on a schedule on a live server.
  • What it changes. Registering a task writes a new entry to the Task Scheduler Library. That is reversible — you delete the task with Unregister-ScheduledTask (shown at the end). What is not reversible is whatever your script does when it fires as SYSTEM. A destructive script run as SYSTEM has nothing stopping it, so treat the payload with the caution its privilege deserves.
  • SYSTEM has no network identity of its own. As SYSTEM the task authenticates to remote resources as the computer account (DOMAIN\MACHINE$), not as a user. Mapped drives, user profile paths, and credentials stored per-user are not available. If your script needs a specific user's credentials or a UNC share the machine account can't reach, SYSTEM is the wrong principal — use a dedicated service account instead.

I'm assuming Windows Server 2022, an elevated PowerShell 5.1 session, and a script already saved on disk that you want to schedule.

The two supported ways

There are two mainstream, well-established tools for this: the ScheduledTasks PowerShell module (Register-ScheduledTask and friends) and the classic schtasks.exe. I'll use PowerShell as the primary path because it's clearer and scriptable, and show the schtasks.exe equivalent for anyone who prefers it.

The GUI (Task Scheduler → Create Task) can do the "highest privileges" part with its Run with highest privileges checkbox, but the GUI's Change User or Group dialog is the fiddly place to enter SYSTEM. For SYSTEM specifically, the command line is the cleaner route.

Create the task in PowerShell

Save your script somewhere sensible — I'll use C:\Scripts\maintenance.ps1 as the placeholder; replace it with your real path. Then build the task from four objects: an action, a trigger, a principal, and (optionally) settings.

# Run in an ELEVATED PowerShell session.

# What the task runs. Adjust the path and arguments to your script.
$action = New-ScheduledTaskAction `
    -Execute "powershell.exe" `
    -Argument "-NoProfile -ExecutionPolicy Bypass -File `"C:\Scripts\maintenance.ps1`""

# When it runs. Daily at 02:00 here — change to suit.
$trigger = New-ScheduledTaskTrigger -Daily -At 2:00AM

# WHO it runs as: SYSTEM, non-interactive, with the highest (unfiltered) token.
$principal = New-ScheduledTaskPrincipal `
    -UserId "SYSTEM" `
    -LogonType ServiceAccount `
    -RunLevel Highest

# Optional but sensible: let it run even on battery, and start if a scheduled run was missed.
$settings = New-ScheduledTaskSettingsSet `
    -AllowStartIfOnBatteries `
    -DontStopIfGoingOnBatteries `
    -StartWhenAvailable

# Register it under an obvious name. Replace the name to suit your naming convention.
Register-ScheduledTask `
    -TaskName "Nightly Maintenance" `
    -Action $action `
    -Trigger $trigger `
    -Principal $principal `
    -Settings $settings `
    -Description "Runs maintenance.ps1 as SYSTEM with highest privileges."

A few things worth knowing:

  • -UserId "SYSTEM" is the short form Windows accepts for NT AUTHORITY\SYSTEM. -LogonType ServiceAccount is the correct logon type for the built-in service accounts (SYSTEM, LOCAL SERVICE, NETWORK SERVICE) and needs no password.
  • -RunLevel Highest is the exact equivalent of the GUI's Run with highest privileges checkbox. For SYSTEM this is somewhat academic — SYSTEM is already fully privileged — but it's the correct flag and matters if you ever switch the principal to an admin user.
  • -ExecutionPolicy Bypass on the powershell.exe call keeps the script from being blocked by machine execution policy without changing policy globally. If your environment forbids this, sign your script instead.

The schtasks.exe equivalent

If you'd rather use the classic tool, this creates the same task. Run it from an elevated command prompt:

schtasks /Create ^
  /TN "Nightly Maintenance" ^
  /TR "powershell.exe -NoProfile -ExecutionPolicy Bypass -File \"C:\Scripts\maintenance.ps1\"" ^
  /SC DAILY /ST 02:00 ^
  /RU "SYSTEM" ^
  /RL HIGHEST

Here /RU "SYSTEM" sets the run-as account (no /RP password needed for SYSTEM), and /RL HIGHEST is the highest-privileges level. This is a well-documented tool; for the full flag list see Microsoft Learn's schtasks reference.

Verify it worked

First, confirm the task exists and check how it's configured:

Get-ScheduledTask -TaskName "Nightly Maintenance" |
    Select-Object TaskName, State,
        @{n='RunAs';   e={$_.Principal.UserId}},
        @{n='RunLevel';e={$_.Principal.RunLevel}}

You want to see RunAs = SYSTEM and RunLevel = Highest.

Now run it once on demand rather than waiting for the schedule, and check the result:

Start-ScheduledTask -TaskName "Nightly Maintenance"

# Wait a moment for it to finish, then inspect the outcome.
Get-ScheduledTaskInfo -TaskName "Nightly Maintenance" |
    Select-Object LastRunTime, LastTaskResult, NextRunTime

LastTaskResult of 0 means the task exited successfully. Non-zero values are exit codes from what the task ran — chase those in your script, not the scheduler. You can also open Task Scheduler (taskschd.msc), find the task in the Library, and read the History tab and the Last Run Result column for the same information in the GUI.

Because SYSTEM runs with no interactive desktop, don't expect to see anything on screen. Have your script write to a log file or the Windows Event Log so you have proof it ran and did what you expected.

Undo / roll back

Removing the task is a single command in the elevated session:

Unregister-ScheduledTask -TaskName "Nightly Maintenance" -Confirm:$false

Or with the classic tool:

schtasks /Delete /TN "Nightly Maintenance" /F

Deleting the task removes only the schedule entry — it does not undo anything the task already did on previous runs. If your script made changes you need to reverse, that's a separate job.

When SYSTEM is the wrong answer

If the task needs to reach a specific network share, use domain credentials, or touch a user's profile, don't reach for SYSTEM. Create a dedicated (ideally a Group Managed Service Account) and register the task under that principal instead — the New-ScheduledTaskPrincipal step is where you'd swap the identity. SYSTEM is for local, machine-scoped work; keep it in that lane.

Written by
Ketan Aagja

Runs enterprise networks and security for a living, and writes Shore Up to turn two decades of hands-on Linux, Windows and mail-server work into guides you can actually use.

More about the author →

Was this article helpful?

Tap a star — no sign-in needed.

Be the first to rate this article.

Automate Print-Queue Cleanup and Clearing Stuck Jobs

Stuck print jobs are one of those recurring, low-glamour tickets: a queue jams, one document sits in "Error" or "Deleting" forever, and everything behind it stalls. This guide gives you two standard tools — a graceful per-job cleanup and the classic spooler reset — and shows how to run them safely on a schedule.

9 min read

Detect and Quarantine Suspicious File Extensions in Shared Folders

This guide builds a PowerShell script that scans an SMB shared folder for files whose extensions are commonly used to carry malware ( .exe , .scr , .js , .vbs , .bat , and so on), and moves any it finds into a locked-down quarantine folder outside the share, writing every action to a log. It is a crude, extension-based screen — a tripwire, not an antivirus engine. It does not inspect file contents and will not catch a malicious .docm or a renamed payload. Treat it as one layer, not the layer.

9 min read