I am trying to create a task that essentially reboots the server but the task is put there by a remote server running a check to see if a reboot is needed. I am stuck trying
This error is caused by a bug introduced in the Task Scheduler back in Vista. Read more here "The task XML is missing a required element or attribute" error when you use the /z switch together with the Schtasks command in Windows Vista
The work around is to create a task compatible with pre-Windows Vista platforms, if you want to use the -DeleteExpiredTaskAfter parameter.
This is
-Compatibility V1
Or in your code
$settings = New-ScheduledTaskSettingsSet -Compatibility V1 -DeleteExpiredTaskAfter 00:00:01 -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries -ExecutionTimeLimit 01:00:00 -RestartCount 3
There are other things to look out for when setting a scheduled task to expire.
Valid values.
There is only a limited number of values that are valid a parameter "-DeleteExpiredTaskAfter". These are Imediately (PT0S), 30 days (P30D), 90 days (P90D), 180 days (P180D) and 365 days (P365D).
Other Prerequisites
Before a task can be set to expire there has to be a trigger with an expiration time (EndBoundary) set.
This is where I'm stuck at the moment, because new PS4.0 cmdlets don't seem to cater for this.
In the meantime here is an "old school" way of setting up a scheduled task directly via Scheduler Service COM interface.
$server = "...."
$domain = $server
$user = "...."
$password = "...."
$ExecuteTime = (Get-Date).AddDays(1)
$ExpireTime = $ExecuteTime.AddMinutes(1)
$taskname = "Reboot $server"
$taskpath = "PendingReboots"
$taskdesc = "Server Reboot"
$ShedService = new-object -comobject "Schedule.Service"
$ShedService.Connect($server, $user, $domain, $password)
$Task = $ShedService.NewTask(0)
$Task.RegistrationInfo.Description = "$taskdesc"
$Task.Settings.Enabled = $true
$Task.Settings.AllowDemandStart = $true
$Task.Settings.DeleteExpiredTaskAfter = "PT0S"
$Task.Settings.ExecutionTimeLimit = "PT1H"
$Task.Settings.StopIfGoingOnBatteries = $false
$Task.Settings.RestartCount = 3
$trigger = $task.triggers.Create(1) # Creates a "One time" trigger
# TASK_TRIGGER_EVENT 0
# TASK_TRIGGER_TIME 1
# TASK_TRIGGER_DAILY 2
# TASK_TRIGGER_WEEKLY 3
# TASK_TRIGGER_MONTHLY 4
# TASK_TRIGGER_MONTHLYDOW 5
# TASK_TRIGGER_IDLE 6
# TASK_TRIGGER_REGISTRATION 7
# TASK_TRIGGER_BOOT 8
# TASK_TRIGGER_LOGON 9
# TASK_TRIGGER_SESSION_STATE_CHANGE 11
$trigger.StartBoundary = $ExecuteTime.ToString("yyyy-MM-dd'T'HH:mm:ss")
$trigger.EndBoundary = $ExpireTime.ToString("yyyy-MM-dd'T'HH:mm:ss")
$trigger.Enabled = $true
$Action = $Task.Actions.Create(0)
$action.Path = "shutdown.exe"
$action.Arguments = " -r -f -t 0"
Try {$taskFolder = $ShedService.GetFolder("\$taskpath")}
catch {$taskFolder = $ShedService.GetFolder("\").CreateFolder("$taskpath")}
$result = $taskFolder.RegisterTaskDefinition("$TaskName",$Task,6,"System",$null,5)
This will create the task and then update it with the expires and delete values allowing the EndBoundary
value to be set. The below example sets both the expiration and deletion to 30 days from value of $run
.
Make sure to set the run time to at least one min in the future to allow the task to be update before it runs.
$run = (Get-Date).AddMinutes(1);
$action = New-ScheduledTaskAction -Execute 'Powershell.exe' -Argument 'C:\temp\install-<whateveryouneed>.ps1' -WorkingDirectory 'C:\temp'
$trigger = New-ScheduledTaskTrigger -Once -At $run
$settings = New-ScheduledTaskSettingsSet -Compatibility Win8
Register-ScheduledTask -Action $action -Trigger $trigger -TaskName "Install
from scheduled task" -Description "Installs stuff from a scheduled task as system" -User "system" -Settings $settings
$task = (Get-ScheduledTask -TaskName "Install
from scheduled task")
$task.Triggers[0].EndBoundary = $run.AddDays(30).ToString('s')
$task.Settings.DeleteExpiredTaskAfter = "P30D"
Set-ScheduledTask -InputObject $task
With some help from Craig Duff, here is how you can create a task that is deleted after being run without the compatibility flag and using the PS4.0 cmdlets:
$run = (Get-Date).AddMinutes(2) # Two minutes from now
Register-ScheduledTask -TaskName "MyTask" -User "Domain\User" -InputObject (
(
New-ScheduledTask -Action (
New-ScheduledTaskAction -Execute "C:\path\to\your.exe" -Argument (
"many" +
"arguments " +
"""with quotes"" "
)
) -Trigger (
New-ScheduledTaskTrigger -Once -At ($run.TimeOfDay.ToString("hh\:mm")) # As a "TimeOfDay" to get 24Hr format
) -Settings (
New-ScheduledTaskSettingsSet -DeleteExpiredTaskAfter 00:00:01 # Delete one second after trigger expires
)
) | %{ $_.Triggers[0].EndBoundary = $run.AddMinutes(60).ToString('s') ; $_ } # Run through a pipe to set the end boundary of the trigger
)
The thing is that the trigger must have an EndBoundary defined so that the task can be deleted. The New-ScheduledTaskTrigger cmdlet doesn't have a parameter to define it. The trick is then to create the task and register it in two different steps, with a step in between to define the End Boundary of the trigger. Obviously if your task has more than one trigger, you would need to set the End Boundary for each.
This specific example creates a task that will run c:\path\to\your.exe
once called MyTask
two minutes into the future, running with credentials Domain\User
. The trigger will expire an hour after the execution start (just so someone could verify if it was run from the scheduled tasks window, instead of browsing through the windows logs), and the task will be deleted one second after that.