Task Scheduler - Com.Object Add State Change Trigger

Hello!

I want to add a new trigger to an existing task for when a user unlocks their workstation.

I know i need to use the COM interface to do this, and i know it is state trigger 11 with state change 8.

I have successfully created the trigger object, but i do not seem to be able to save the task once the new trigger is added. I am fairly sure this is just a syntax issue i am not getting, but i need some fresh eyes!

$scheduler = New-Object -ComObject Schedule.Service
$scheduler.connect("Localhost")
$taskFolder = $scheduler.Getfolder("\")
$taskDefinition = $taskFolder.getTasks(1)
$taskDefinition = $taskDefinition | Where-Object { $_.Name -eq "Random Task Name" }
$regInfo = $TaskDefinition.Definition.RegistrationInfo
$settings = $taskDefinition.Definition.Settings
$Triggers = $TaskDefinition.Definition.Triggers
$Trigger = $Triggers.Create(11)
$trigger.StateChange = 8
$trigger.Enabled = $true
$trigger.ID = 2

So at this point, you can see b0th triggers present in $triggers, but then how do i commit that?

What version of Powershell is the workstation running? If Workstations are running Powershell 4.0, then there are cmdlets you can use rather than messing with an old COM object:

https://blog.netwrix.com/2018/07/03/how-to-automate-powershell-scripts-with-task-scheduler/

Windows 10, 5.1

which cmdlet?

 

New-schduledtasktrigger doesn’t seem to support state change triggers.

The trigger 8 is ‘On Startup’, so a comparable start would be:

New-ScheduledTaskTrigger -AtLogOn ...

https://docs.microsoft.com/en-us/windows/win32/taskschd/sessionstatechangetrigger-statechange

StateChange 8 is not ‘on startup’, and not under Session State Change. https://docs.microsoft.com/en-us/windows/win32/taskschd/sessionstatechangetrigger

 

OK i have decided to just work around the issue.

In the script, i have an object containing the task definitions i want to create, currently only one has the ‘on unlock’ requirement.

$scheduledtasks = @(
[pscustomobject]@{
Path = "\"
Name = "My Task"
Program = "C:\Windows\System32\WindowsPowerShell\v1.0\PowerShell.exe"
Arguments = "-noLogo -windowStyle hidden -command C:\users\public\logonscript.ps1 -randomswitch"
Trigger = @(
[pscustomobject]@{
TriggerType = 9
}
[pscustomobject]@{
TriggerType = 11
StateChange = 8
}
)
Principal = "BUILTIN\Users"
LogonType = 4
Repeat = ""
UseCom = 1
}
[pscustomobject]@{
Path = "\"
Name = "Different Task 2
Program = "C:\Windows\System32\WindowsPowerShell\v1.0\PowerShell.exe"
Arguments = "-noLogo -windowStyle hidden -command C:\users\public\othertask.ps1"
Trigger = New-ScheduledTaskTrigger -AtLogon -RandomDelay (New-TimeSpan -hours 1)
Principal = New-ScheduledTaskPrincipal -GroupID Users -runlevel Limited -id Author
}
}
Then after that a foreach loop to check for, and create if required each task in $scheduledTasks.
So as above i have just added some additional fields to the object, UseCom = 1 and Triggers.
foreach ($schTask in $scheduledtasks) {
if (!(Get-ScheduledTask $schTask.Name -ErrorAction SilentlyContinue)) {
if (($schtask.UseCom) -eq 1) {
# Create Task Using Com Object
$scheduler = New-Object -ComObject Schedule.Service
$scheduler.connect("Localhost")
$newTask = $scheduler.NewTask(0)
$ri = $newTask.RegistrationInfo
$ri.Description = $schTask.Name
$ri.Author = "Me"
$principal = $newTask.principal
$principal.LogonType = $schTask.LogonType
$principal.GroupID = $schTask.Principal
$principal.RunLevel = 0
foreach ($trigger in $schTask.Trigger) {
$trigger = $newtask.Triggers.Create($trigger.TriggerType)
if ($trigger.StateChange) {
$trigger.StateChange = 8
}
$trigger.Enabled = $true
}
$action = $newTask.Actions.Create(0)
$action.Path = $schTask.Program
$action.Arguments = $schTask.Arguments
$taskFolder = $scheduler.Getfolder("\")
try {
$taskFolder.RegisterTaskDefinition($schTask.Name, $newTask, 6, "", "", 3, $null)
}
catch {
Write-Warning "Error creating task"
}
}
else {
# Create Task Using PowerShell
$taskPath = $schTask.Path
$taskName = $schTask.Name
$program = $schTask.Program
$arguments = $schTask.Arguments
Write-Output "Creating Scheduled Task : $taskName"
$a = New-ScheduledTaskAction -Execute $program -Argument $arguments
$t = $schTask.Trigger
$p = $schTask.Principal
$s = New-ScheduledTaskSettingsSet
$d = New-ScheduledTask -Action $A -Principal $p -Trigger $T -Settings $S
try {
$task = Register-ScheduledTask -InputObject $d -TaskPath $taskPath -TaskName $taskName -ErrorAction Stop
if ($schTask.Repeat) {
$task.Triggers.Repetition.Interval = $schTask.Repeat
$task | Set-ScheduledTask | Out-Null
}
}
catch {
Write-Warning "Error Registering Scheduled Task : $taskName"
}
}
}
}
Not ideal, but gets the job done.

Here are all 8 of the SessionState triggers:

Value Meaning
<dl> <dt>TASK_CONSOLE_CONNECT</dt> <dt>1</dt> </dl> Terminal Server console connection state change. For example, when you connect to a user session on the local computer by switching users on the computer.
<dl> <dt>TASK_CONSOLE_DISCONNECT</dt> <dt>2</dt> </dl> Terminal Server console disconnection state change. For example, when you disconnect to a user session on the local computer by switching users on the computer.
<dl> <dt>TASK_REMOTE_CONNECT</dt> <dt>3</dt> </dl> Terminal Server remote connection state change. For example, when a user connects to a user session by using the Remote Desktop Connection program from a remote computer.
<dl> <dt>TASK_REMOTE_DISCONNECT</dt> <dt>4</dt> </dl> Terminal Server remote disconnection state change. For example, when a user disconnects from a user session while using the Remote Desktop Connection program from a remote computer.
<dl> <dt>TASK_SESSION_LOCK</dt> <dt>7</dt> </dl> Terminal Server session locked state change. For example, this state change causes the task to run when the computer is locked.
<dl> <dt>TASK_SESSION_UNLOCK</dt> <dt>8</dt> </dl> Terminal Server session unlocked state change. For example, this state change causes the task to run when the computer is unlocked.
Source: https://docs.microsoft.com/en-us/windows/win32/taskschd/sessionstatechangetrigger-statechange