Skip to content

Instantly share code, notes, and snippets.

@jhuckaby
Created January 26, 2026 19:46
Show Gist options
  • Select an option

  • Save jhuckaby/08fe5b31e79edafd2d480c96c260334f to your computer and use it in GitHub Desktop.

Select an option

Save jhuckaby/08fe5b31e79edafd2d480c96c260334f to your computer and use it in GitHub Desktop.

1) Install-time: create the task (runs as User X)

$TaskName = "xyOps-RunAsUser"
$User     = "DOMAIN\User"   # or "$env:COMPUTERNAME\User"
$Wrapper  = "C:\ProgramData\xyOps\runas\wrapper.ps1"

New-Item -Force -ItemType Directory (Split-Path $Wrapper) | Out-Null

@'
param(
  [string]$RequestPath = "C:\ProgramData\xyOps\runas\request.json"
)

# Read request
$req = Get-Content -Raw -LiteralPath $RequestPath | ConvertFrom-Json

# Basic validation (tighten this to your needs)
if (-not $req.mode) { throw "Missing mode" }

# Execute
switch ($req.mode) {
  "command" {
    # Runs via cmd.exe to keep it simple, supports user-defined exe/args
    $p = Start-Process -FilePath $req.file -ArgumentList $req.args -WorkingDirectory $req.cwd -PassThru
    $p.WaitForExit()
    exit $p.ExitCode
  }
  "pwsh" {
    # Execute a PowerShell script file with args
    $argList = @("-NoProfile","-ExecutionPolicy","Bypass","-File",$req.file) + @($req.args)
    $p = Start-Process -FilePath "pwsh.exe" -ArgumentList $argList -WorkingDirectory $req.cwd -PassThru
    $p.WaitForExit()
    exit $p.ExitCode
  }
  default { throw "Unknown mode: $($req.mode)" }
}
'@ | Set-Content -LiteralPath $Wrapper -Encoding UTF8

# Task action: always runs wrapper, wrapper reads request.json
$Action    = New-ScheduledTaskAction -Execute "pwsh.exe" -Argument "-NoProfile -ExecutionPolicy Bypass -File `"$Wrapper`""
$Principal = New-ScheduledTaskPrincipal -UserId $User -LogonType Password -RunLevel Limited
$Settings  = New-ScheduledTaskSettingsSet -MultipleInstances IgnoreNew -StartWhenAvailable

# Dummy trigger required to register; we will run it on-demand
$Trigger   = New-ScheduledTaskTrigger -AtLogOn

$Task      = New-ScheduledTask -Action $Action -Principal $Principal -Trigger $Trigger -Settings $Settings

$SecurePass = Read-Host "Password for $User" -AsSecureString
Register-ScheduledTask -TaskName $TaskName -InputObject $Task -User $User -Password $SecurePass

Notes:

  • RunLevel Limited = non-elevated as that user (change to Highest if you actually want elevation).
  • The task always runs the wrapper; you change what it does by changing the request file.

2) Runtime: write the request + trigger the task

Run a PowerShell script as the target user

$RequestPath = "C:\ProgramData\xyOps\runas\request.json"
$TaskName    = "xyOps-RunAsUser"

$req = @{
  mode = "pwsh"
  file = "C:\Path\To\UserScript.ps1"
  args = @("arg1","arg2")
  cwd  = "C:\Path\To"
}

($req | ConvertTo-Json -Depth 5) | Set-Content -LiteralPath $RequestPath -Encoding UTF8

Start-ScheduledTask -TaskName $TaskName

3) The one gotcha you’ll hit immediately (concurrency)

With a single fixed request.json, two simultaneous launches will collide.

If you need concurrency, use a unique request file per user.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment