Complex Simplicity

Simple solutions for complex problems.

How to Deploy SYSTEM Context Scheduled Tasks in Microsoft Intune

There are moments when working with Microsoft Intune makes me miss the simplicity of classic Group Policy Objects (GPOs), especially the Logon/Logoff Scripts feature. If you wanted to trigger an action at logon, startup, or any other event, GPO scripts were perfect.

You could argue that Intune Remediation Scripts fill part of that gap. While they are a solid replacement for certain scenarios, they run on a schedule, not on an event trigger. On top of that, many Microsoft 365 tenants license users with Microsoft 365 Business Premium, which does not include remediation scripts at all.

Fortunately, we do have an alternative: Scheduled Tasks. By creating scheduled tasks on endpoints – and binding them to system or user events – we can recreate much of the old GPO behavior. The only catch is that we must deploy them using PowerShell combined with a Win32 app in Intune.

In this post, I’ll walk you through creating a scheduled task that runs in SYSTEM context. If you’re looking for a scheduled task that runs under USER context, I’ve covered that in a separate article which you can find here.

All source files are available on my GitHub page.

Required Files for Deploying a Scheduled Task with a Win32 App

To deploy a scheduled task through Intune using a Win32 app, you’ll need the following files:

  • install.ps1 – Creates the scheduled task.
  • uninstall.ps1 – Removes the scheduled task when it’s no longer needed.
  • task-system.ps1 – Contains the actions the scheduled task will execute.
  • detection.ps1 – Detects whether the scheduled task is installed (Intune uses this for Win32 app detection rules).
Creating the files

Let’s start by preparing the folder structure and files needed for the Intune Win32 app that will deploy our SYSTEM‑level scheduled task.

source
o install.ps1
o uninstall.ps1
- files
o task-system.ps1

Install.ps1

With the required structure in place, we can begin writing the installation logic. This script will:

  • Ensure no duplicate tasks exist
  • Create the scheduled task
  • Copy task-system.ps1 to a location accessible by the task

We start with a few basic checks. The scheduled task needs access to the PowerShell script, so we create a directory under C:\ProgramData. If the directory doesn’t exist, the script will create it.

# The directory where we store the script which has to be run by the scheduled task
$basePath = "$env:ProgramData\ComSim-ScheduledTask-System\"

# Script used by the scheduled task 
$systemScript  = Join-Path -Path "$BasePath" -ChildPath "task-system.ps1"

# Create the directory if it does not exists
if (!(Test-Path -Path $basePath)) {
    New-Item `
        -Path $basePath `
        -ItemType Directory `
        -Force | Out-Null
}

Next, we verify whether a scheduled task with the same name already exists. This can happen if multiple versions of the Win32 app are deployed (for example, version 1 and version 2). If the task is not present, we copy task-system.ps1 from the files directory into the ProgramData folder.

# Remove the scheduled task if it is present. We will create a new one with the same name.
Unregister-ScheduledTask `
    -TaskName "ComSim - ScheduledTask System" `
    -Confirm:$false `
    -ErrorAction SilentlyContinue

# Copy the script we want to run with the scheduled task to the directory
Copy-Item `
    (Join-Path -Path "$PSScriptRoot\files" -ChildPath "task-system.ps1") `
    $systemScript `
    -Force

Now that the script is in place and we’ve confirmed no conflicting tasks exist, we can create our new scheduled task.

We define:

  • Action – What the task should execute (in this case, a PowerShell script).
  • Trigger – When the task should run (here: at logon of any user).
  • TaskSettings – General configuration such as allowing the task to run on battery power.

Once these components are defined, we register the task using Register-ScheduledTask.

# 1. Lets define the action, we want to run PowerShell which will then execute the task in the given location
$action = New-ScheduledTaskAction `
    -Execute "powershell.exe" `
    -Argument "-NoProfile -WindowStyle Hidden -ExecutionPolicy Bypass -File `"$systemScript`""

# 2. The action that will trigger the scheduled task, a logon of (any) user 
$trigger = New-ScheduledTaskTrigger -AtLogOn

# 3. Settings for the task::
# -StartWhenAvailable: Indicates that Task Scheduler can start the task at any time after its scheduled time has passed.
# -DontStopIfGoingOnBatteries: Indicates that the task does not stop if the computer switches to battery power.
# -AllowStartIfOnBatteries: Indicates that Task Scheduler starts if the computer is running on battery power. It will still run if its plugged in ;)
$taskSettings = New-ScheduledTaskSettingsSet `
    -AllowStartIfOnBatteries `
    -DontStopIfGoingOnBatteries `
    -StartWhenAvailable      

# 4. Register the scheduled task
# -User: This will indicate that it will run under SYSTEM context    
# -RunLevel: Run at the highest privilege level
Register-ScheduledTask `
-TaskName "ComSim - ScheduledTask System" `
-Action $action `
-Trigger $trigger `
-Settings $taskSettings `
-User "SYSTEM" `
-RunLevel Highest `
-Description "Complex Simplicity scheduled task" `
-Force

Please note: Scheduled tasks created through Microsoft Intune do not appear in the Windows Task Scheduler UI. However, you can verify their presence using an elevated PowerShell session: Get-ScheduledTask -TaskName "ComSim - ScheduledTask System"

Full install.ps1 script
The complete script looks like this:

# The directory where we store the script which has to be run by the scheduleded task
$basePath = "$env:ProgramData\ComSim-ScheduledTask-System\"

# Script used by the scheduled task 
$systemScript  = Join-Path -Path "$BasePath" -ChildPath "task-system.ps1"

# Create the directory if it does not exists
if (!(Test-Path -Path $basePath)) {
    New-Item `
        -Path $basePath `
        -ItemType Directory `
        -Force | Out-Null
}

# Remove the scheduled task if it is present. We will create a new one with the same name.
Unregister-ScheduledTask `
    -TaskName "ComSim - ScheduledTask System" `
    -Confirm:$false `
    -ErrorAction SilentlyContinue

# Copy the script we want to run with the scheduled task to the directory
Copy-Item `
    (Join-Path -Path "$PSScriptRoot\files" -ChildPath "task-system.ps1") `
    $systemScript `
    -Force

# Everything is in place, lets create the scheduled task      

# 1. Lets define the action, we want to run PowerShell which will then execute the task in the given location
$action = New-ScheduledTaskAction `
    -Execute "powershell.exe" `
    -Argument "-NoProfile -WindowStyle Hidden -ExecutionPolicy Bypass -File `"$systemScript`""

# 2. The action that will trigger the scheduled task, a logon of (any) user 
$trigger = New-ScheduledTaskTrigger -AtLogOn

# 3. Settings for the task::
# -StartWhenAvailable: Indicates that Task Scheduler can start the task at any time after its scheduled time has passed.
# -DontStopIfGoingOnBatteries: Indicates that the task does not stop if the computer switches to battery power.
# -AllowStartIfOnBatteries: Indicates that Task Scheduler starts if the computer is running on battery power. It will still run if its plugged in ;)
$taskSettings = New-ScheduledTaskSettingsSet `
    -AllowStartIfOnBatteries `
    -DontStopIfGoingOnBatteries `
    -StartWhenAvailable      

# 4. Register the scheduled task
# -User: This will indicate that it will run under SYSTEM context    
# -RunLevel: Run at the highest privilege level
Register-ScheduledTask `
-TaskName "ComSim - ScheduledTask System" `
-Action $action `
-Trigger $trigger `
-Settings $taskSettings `
-User "SYSTEM" `
-RunLevel Highest `
-Description "Complex Simplicity scheduled task" `
-Force

# Please note, if this task is created using Microsoft Intune, it will not show up in the Task Scheduler. 
# It will be however possible to see the task running a elevated powershell cmdlet; Get-Scheduled Task-TaskName "ComSim - ScheduledTask System"
Uninstall.ps1

Just like any Intune Win32 app, our installation script needs a matching uninstall.ps1 to properly remove everything we created. The logic in this file is straightforward: we delete the folder ComSim-ScheduledTask-System in C:\ProgramData (including all files inside it) and remove the scheduled task itself.

# The directory where we stored the script which had to be run by the scheduleded task
$basePath = "$env:ProgramData\ComSim-ScheduledTask-System\"

# Remove the scheduled task if it is present.
Unregister-ScheduledTask `
    -TaskName "ComSim - ScheduledTask System" `
    -Confirm:$false `
    -ErrorAction SilentlyContinue

# Remove the directory and all the files within
Remove-Item -Path "$basePath" -Recurse -Force
Task-system.ps1

The task-system.ps1 file contains the logic you want to execute under SYSTEM context whenever a user logs on to the device. This is where you define the actual action your scheduled task will perform.

In the example below, the script appends the current date to a text file each time the scheduled task runs. This is just a simple demonstration, feel free to replace this logic with any action you want to perform at logon.

# Replace this with code with your own code :)
$path = "C:\Temp\ComSim.txt"

# Create a file and directory
if (!(Test-Path $path)) {
    New-Item -ItemType File -Path $path -Force | Out-Null
}

# Append the current date
(Get-Date) | Add-Content -Path $path

Creating the Win32 App

Now that all required files and folders are in place, we can package everything into a Microsoft Intune Win32 app using the IntuneWinAppUtil tool. This tool converts your source folder into a .intunewin file, which Intune can deploy to your endpoints.

Detection.ps1

Every Intune Win32 app requires a detection method to verify whether the application (or in this case, the scheduled task) is installed. Since we cannot reliably check for a file or registry key, we use a simple detection script.

This script checks whether the scheduled task exists on the device. If the task is present, Intune will mark the Win32 app as installed. If not, Intune will trigger the installation logic again.

$taskName = "ComSim - ScheduledTask System"

$task = Get-ScheduledTask -TaskName $taskName -ErrorAction SilentlyContinue

if ($task) {
    # Task exists > detection success
    exit 0
} else {
    # Task not found > detection failed
    exit 1
}
Creating the Win32 App in Intune

After packaging your .intunewin file, upload it to Microsoft Intune. Once uploaded, configure the app with the following commands:

Install command: %SystemRoot%\Sysnative\WindowsPowerShell\v1.0\powershell.exe -ExecutionPolicy Bypass -WindowStyle Hidden -file .\install.ps1

Uninstall command: %SystemRoot%\Sysnative\WindowsPowerShell\v1.0\powershell.exe -ExecutionPolicy Bypass -WindowStyle Hidden -file .\uninstall.ps1

Using Sysnative ensures the script runs correctly in 64‑bit PowerShell, even when invoked from a 32‑bit Intune process.

Set the Install behavior to System. This is required because the scheduled task runs in SYSTEM context, and the installation script must also execute with system‑level permissions. After configuring detection rules (using your detection.ps1 script) and assigning the app to the appropriate devices or groups, Intune will deploy the scheduled task automatically.