Powershell – How to run a Windows Task that executes a PowerShell script as the Windows Local Service account

powershelltask-schedulerwindows-server-2008-r2

On a 2008 R2 server, I have a Windows task that executes a PowerShell script. It works fine when set in Task Scheduler to execute as an Admin account, but I'd like to set it to execute as the Windows Local Service account instead as I'd read that it's considered a security best practice since Local Service has very limited privileges.

When using the Local Service account, the task itself appears to run the PowerShell script successfully according to the history log in the task scheduler. But the PowerShell script does not output a text file as it's designed to do, leading me to believe that perhaps the PowerShell.exe itself ran, but the script was blocked for some reason.

In the task scheduler, I have the task set to "Run with highest privileges" so thought that was all I needed to get it to run successfully…

How can I get this PowerShell task to run successfully? Or should I just create a Windows domain service account to run this task instead?

Best Answer

I prefer to use Register-ScheduledJob as it allows me to use PowerShell script blocks instead of script files. But you are free to use only Register-ScheduledTask that will be even easier.

In the script below I use Register-ScheduledJob to create a PowerShell job.
Then I use Set-ScheduledTask to change the startup account to LocalSystem or any another built-in account.

You can run the script multiple times. But run it under an administrative account.

See $accountId = "NT AUTHORITY\LOCAL SERVICE"; below.
Also pay attention to -RunElevated. I've commented out it for you and that works.

$ErrorActionPreference = 'Stop'


Clear-Host

$taskName = "it3xl_dummy_PowerShell_job"
# Unregister-ScheduledJob it3xl_dummy_PowerShell_job -Confirm:$false

$task = Get-ScheduledJob -Name $taskName  -ErrorAction SilentlyContinue
if ($task -ne $null)
{
    Unregister-ScheduledJob $task  -Confirm:$false
    Write-Host "Old $taskName job has been unregistered"; Write-Host;
}


$trigger = New-JobTrigger -AtStartup;

$options = New-ScheduledJobOption -StartIfOnBattery  #-RunElevated;

Write-Host "Registering new $taskName job";
Register-ScheduledJob -Name $taskName  -Trigger $trigger  -ScheduledJobOption $options `
    -ScriptBlock {
    Write-Host In our PowerShell job we say - oppa!;
}


#$accountId = "NT AUTHORITY\SYSTEM";
$accountId = "NT AUTHORITY\LOCAL SERVICE";
$principal = New-ScheduledTaskPrincipal -UserID $accountId `
    -LogonType ServiceAccount  -RunLevel Highest;

$psSobsSchedulerPath = "\Microsoft\Windows\PowerShell\ScheduledJobs";
$someResult = Set-ScheduledTask -TaskPath $psSobsSchedulerPath `
    -TaskName $taskName  -Principal $principal


Write-Host;
Write-Host "Let's show proofs that our PowerShell job will be running under the LocalSytem account"
$task = Get-ScheduledTask -TaskName $taskName
$task.Principal

Write-Host "Let's start $taskName"
Start-Job -DefinitionName $taskName | Format-Table

Write-Host "Let's proof that our PowerShell job was ran"
Start-Sleep -Seconds 3
Receive-Job -Name $taskName