Scheduled Powershell Scripts without storing credentials

Sometimes I want to schedule a script to run with specific domain credentials without storing anything blatantly risky. For example here I wanted to schedule maintenance notifications to users that have been logged in for so long that their hosts are up for replacement…

Easiest way is to set up a GMSA and use it for the scheduled tasks, the only caveat is that you can’t select a G/MSA account for those tasks in the task scheduler UI. Hmm.

The workaround is to set the task account from either SC or PowerShell; however I also wanted to script the rest of the task setup. Nobody likes instructions that involve “Do a dozen things by hand in the UI, then write some lines to modify it”.

The next step to low footprint bliss would be to say goodbye to all the files and ACLs, let’s just inline the scripts (if they’re short enough)! So here’s my script to create a file-less, credential-less (kind of) PowerShell scheduled task that in this case schedules desktop messages for Citrix sessions. Ironically this specific example drops transcript copies but you get the point.

#Encode script as Base64, send a Citrix message in this example
function EncodeMessageTaskScript ($MessageText, $AdminAddress)
    $TaskScript = @"
    Start-Transcript "C:\ScriptLogs\ScheduledPS.log" -Append
    & { Add-Pssnapin @('Citrix.Host.Admin.V2′,'Citrix.Broker.Admin.V2′)}

    `$CurrentSessions = Get-BrokerSession -AdminAddress "$AdminAddress" -MaxRecordCount 1000 | ? DesktopGroupName -eq 'Nope'
    `$CurrentSessions | % { Send-BrokerSessionMessage -AdminAddress "$AdminAddress" -InputObject `$_ -MessageStyle Critical -Text "$MessageText"`nMessage Sent [`$(Get-Date)]" -Title "Maintenance Warning"}


"@#Let's pretend that this is indented and that you can't end a herestring with whitespace...


function CreateScheduledGMSATask ($EncodedPsScript, [datetime]$TriggerDateTime, $TaskName)

    $Action = New-ScheduledTaskAction -Execute "powershell.exe" -Argument "-EncodedCommand `"$EncodedPsScript`" -NoLogo -NoProfile -ExecutionPolicy Bypass"
    $Principal = New-ScheduledTaskPrincipal -LogonType Password -RunLevel Limited -UserId 'DOMAIN\[G]MSA$'
    $Settings = New-ScheduledTaskSettingsSet -Compatibility Win8
    $Trigger = New-ScheduledTaskTrigger -Once -At $TriggerDateTime

    $TaskObj = New-ScheduledTask -Action $Action -Principal $Principal -Trigger $Trigger -Settings $Settings
    Register-ScheduledTask -TaskName $TaskName -InputObject $TaskObj


$EncodedReminderTask = EncodeMessageTaskScript -MessageText "Your Message Text" -AdminAddress $CitrixAdminAddress
CreateScheduledGMSATask -EncodedPsScript $EncodedReminderTask -TriggerDateTime $MaintReminder -TaskName "MessageTask $($MaintStartTime.ToString('yyyyMMdd.HHmmss'))"