Restart Application in current logged on user context

Detection Script

$taskName = "ApplicationRestart-$env:USERNAME"

schtasks.exe /Query /TN $taskName > $null 2>&1
if ($LASTEXITCODE -eq 0) {
    Write-Output "Scheduled task '$taskName' exists."
    exit 0
} else {
    Write-Output "Scheduled task '$taskName' does NOT exist."
    exit 1
}

Remediation Script

$username = $env:username
$Applicationpath = "C:\Users\$username\AppData\Local\Application\restart"
$ps1FilePath = "$Applicationpath\Application.ps1"

#create directory if it does not exist
if (-not (Test-Path $Applicationpath)) {
    try {
        New-Item -ItemType Directory -Path $Applicationpath | Out-Null
        Write-Output "Created folder: $Applicationpath"
    } catch {
        Write-Output "Failed to create directory $Applicationpath : $_"
        exit 1
    }
}

#create PS1 script
try {
$ps1Content = @'
# Get active session ID
    $activeUser = (query user | Where-Object { $_ -match '^>' }) -replace '^>\s*', '' -replace '\s+.*$', ''

    if ($activeUser) {
        $processName = "ApplicationClient.exe"
        $exePath = "C:\Program Files (x86)\Application\ApplicationClient.exe"

        # Get all processes with owner info
        $processes = Get-WmiObject Win32_Process -Filter "Name = '$processName'" | ForEach-Object {
            $owner = $_.GetOwner()
            if ($owner.User) {
                [PSCustomObject]@{
                    ProcessName = $_.Name
                    UserName    = "$($owner.User)"
                }
            }
        }

        # Check if ApplicationClient is already running by active user    
        $ApplicationRunning = $processes | Where-Object { $_.UserName -eq $activeUser }

        if ($ApplicationRunning) {
            Write-Output "Application is running under active user: $activeUser"
        } else {
            Write-Output "Application is NOT running under active user: $activeUser"
            $command = "`"$exePath`""
            $null = Invoke-WmiMethod -Class Win32_Process -Name Create -ArgumentList $command -ComputerName localhost `
                -Credential $null -Impersonation Impersonate `
                -Namespace "root\cimv2" `
                -Authentication Packet
        }   
    } else {
        Write-Output "No active user session found."
    }
'@
    Set-Content -Path $ps1FilePath -Value $ps1Content
    Write-Output "Successfully created PS1: $ps1FilePath"
} catch {
    Write-Output "Failed to create PS1: $_"
    exit 1
}

#create base XML template with placeholder for UserId
$xmlTemplate = @'
<?xml version="1.0" encoding="UTF-16"?>
<Task version="1.2" xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task">
  <RegistrationInfo>
    <Version>1.0.0.0</Version>
  </RegistrationInfo>
  <Triggers>
    <TimeTrigger>
      <Repetition>
        <Interval>PT15M</Interval>
        <StopAtDurationEnd>false</StopAtDurationEnd>
      </Repetition>
      <StartBoundary>2025-08-04T00:01:00+02:00</StartBoundary>
      <Enabled>true</Enabled>
    </TimeTrigger>
  </Triggers>
  <Principals>
    <Principal id="Author">
      <UserId>{USER_SID}</UserId>
      <LogonType>InteractiveToken</LogonType>
      <RunLevel>LeastPrivilege</RunLevel>
    </Principal>
  </Principals>
  <Settings>
    <MultipleInstancesPolicy>IgnoreNew</MultipleInstancesPolicy>
    <DisallowStartIfOnBatteries>false</DisallowStartIfOnBatteries>
    <StopIfGoingOnBatteries>false</StopIfGoingOnBatteries>
    <AllowHardTerminate>true</AllowHardTerminate>
    <StartWhenAvailable>true</StartWhenAvailable>
    <RunOnlyIfNetworkAvailable>false</RunOnlyIfNetworkAvailable>
    <IdleSettings>
      <StopOnIdleEnd>false</StopOnIdleEnd>
      <RestartOnIdle>false</RestartOnIdle>
    </IdleSettings>
    <AllowStartOnDemand>true</AllowStartOnDemand>
    <Enabled>true</Enabled>
    <Hidden>false</Hidden>
    <RunOnlyIfIdle>false</RunOnlyIfIdle>
    <WakeToRun>false</WakeToRun>
    <ExecutionTimeLimit>PT72H</ExecutionTimeLimit>
    <Priority>7</Priority>
  </Settings>
  <Actions Context="Author">
    <Exec>
      <Command>powershell.exe</Command>
      <Arguments>-ExecutionPolicy Bypass -File "C:\Program Files (x86)\Application\restart\Application.ps1"</Arguments>
    </Exec>
  </Actions>
</Task>
'@

$username = $env:username

#function to get SID for a username
function Get-SIDForUser($username) {
    try {
        $user = New-Object System.Security.Principal.NTAccount($username)
        $sid = $user.Translate([System.Security.Principal.SecurityIdentifier])
        return $sid.Value
    } catch {
        Write-output "Failed to get SID for user $username"
        return $null
    }
}

$userSid = Get-SIDForUser $username

#prepare XML for this user
$userXmlPath = Join-Path $Applicationpath "Application_$username.xml"

#replace placeholder SID and script path
$userXmlContent = $xmlTemplate `
    -replace '{USER_SID}', $userSid `
    -replace 'C:\\Program Files \(x86\)\\Application\\restart\\Application.ps1', "$ps1FilePath"

#save XML (make sure it's Unicode)
try {
  $userXmlContent | Out-File -FilePath $userXmlPath -Encoding Unicode
  Write-Output "Successfully create XML: $userXmlPath"
} catch {
  Write-Output "Failed to create XML: $userXmlPath"
  exit 1
}

#task name should be unique per user
$taskName = "Application-ApplicationRestart-$username"

#create scheduled task for this user
try {
    Write-Output "Creating task for user $username ..."
    $result = & schtasks.exe /Create /TN $taskName /XML "$userXmlPath" /F 2>&1
    Write-Output $result
    exit 0
} catch {
    Write-Output "Failed to create task for $username : $_"
    exit 1
}