<pre class="lang:ps decode:true " title=“Set-ScheduledTask” >
Function Set-ScheduledTask {
<#
.SYNOPSIS
Creates or removes a scheduled task on local computer or remote computer
.DESCRIPTION
Creates a scheduled task on local computer or remote computer using COM object and XML text.
Returns $True or $False
Simply create a task in "Task Scheduler" and export it to XML.
Then use that XML from inside a Here-String [@" "@] or from the xml file
CAVEAT: Only handles task folders ONE level deep
.PARAMETER TaskXmlContent
Contents of a Task XML file as [String], [XmlDocument] or [xml].
.PARAMETER TaskNamePath
Name of the task to be created/removed and/or TaskFolder to be created/removed
Can also contain a TaskFolder. TaskFolder is auto-created if needed.
[TODO SomeDay: Optional if TaskXmlContent Description’s Last line is @@@MyTaskFolder\MyTaskName]
.PARAMETER TaskUser
Default: $null
TaskUser can be $null if task is running as SYSTEM or a group (e.g. BUILT-IN\USERS)
.PARAMETER TaskPwd
Default: $null
TaskPwd can be $null if task is running as SYSTEM or a group (e.g. BUILT-IN\USERS)
.PARAMETER ComputerName
Name of Computer where to create the task. Default: localhost
.PARAMETER Remove
Remove the task specified by TaskNamePath [and ComputerName]
.PARAMETER RemoveTaskFolderIfEmpty
Works with -Remove parameter. Delete folder holding the targeted task.
.PARAMETER ContinueOnError
Continue if an error is encountered. Default: $false.
.EXAMPLE
[XML]$NewTaskXmlFileContent = Get-Content “C:\stuff\ExportedTask.xml”
Set-ScheduledTask -TaskNamePath $NewTaskName -TaskXmlContent $NewTaskXmlFileContent -ComputerName $Computer
Creates task in Root Task folder () from “C:\stuff\ExportedTask.xml”
.EXAMPLE
Set-ScheduledTask -TaskNamePath $NewTaskName -Remove -ComputerName $Computer
Removes task from Root Task folder ()
.EXAMPLE
[xml]$NewTaskXmlContent = @" …<xml stuff>… "@
Set-ScheduledTask -TaskNamePath “MyTaskFolder\MyTaskName” -TaskXmlContent $NewTaskXmlContent
Creates task in MyTaskFolder Task folder (\MyTaskFolder) from [xml]$NewTaskXmlContent
CAVEAT: Cannot create more than ONE level deep.
.EXAMPLE
Set-ScheduledTask -TaskNamePath “MyTaskFolder\MyTaskName” -Remove
Set-ScheduledTask -TaskNamePath “\MyTaskFolder\MyTaskName” -Remove
Removes task from MyTaskFolder Task folder (\MyTaskFolder)
CAVEAT: Cannot delete from more than ONE level deep.
.EXAMPLE
Set-ScheduledTask -TaskNamePath “MyTaskFolder\MyTaskName” -Remove -RemoveTaskFolderIfEmpty
Removes task from MyTaskFolder Task folder (\MyTaskFolder)
Removes MyTaskFolder Task folder if it then becomes empty
CAVEAT: Cannot delete from more than ONE level deep.
.EXAMPLE
Set-ScheduledTask -TaskNamePath “\MyTaskFolder*” -Remove -RemoveTaskFolderIfEmpty
Removes MyTaskFolder Task folder and its Tasks. (BE CAREFUL!!)
CAVEAT: Cannot delete more than ONE level deep.
.NOTES
Version 1.0 (22-APR-2015)
Denis St-Pierre (Ottawa, Canada)
LIMITATION: cannot handle more than one task folder deep
Based on http://psappdeploytoolkit.codeplex.com
For syntax use: Get-Help Set-ScheduledTask
#>
[CmdletBinding()]
Param (
[Parameter(Mandatory=$True)]
[ValidateNotNullorEmpty()]
[string]$TaskNamePath = ‘’,
[Parameter(Mandatory=$false)]
[ValidateNotNullorEmpty()]
$TaskXmlContent,
[Parameter(Mandatory=$false)]
[ValidateNotNullorEmpty()]
[Switch]$Remove,
[Parameter(Mandatory=$false)]
[ValidateNotNullorEmpty()]
[string]$ComputerName = “localhost”,
[Parameter(Mandatory=$false)]
[ValidateNotNullorEmpty()]
[string]$TaskUser = $null,
[Parameter(Mandatory=$false)]
[ValidateNotNullorEmpty()]
[string]$TaskPwd = $null,
[Parameter(Mandatory=$false)]
[ValidateNotNullOrEmpty()]
[switch]$RemoveTaskFolderIfEmpty,
[Parameter(Mandatory=$false)]
[ValidateNotNullOrEmpty()]
[bool]$ContinueOnError = $true
)
Begin {
## Get the name of this function and write header
[string]${CmdletName} = $PSCmdlet.MyInvocation.MyCommand.Name
Write-FunctionHeaderOrFooter -CmdletName ${CmdletName} -CmdletBoundParameters $PSBoundParameters -Header
Remove-Variable TaskFolderPath -ErrorAction SilentlyContinue #Needed for debug to make sense
Try {
[System.__ComObject]$ScheduleObj = New-Object -ComObject("Schedule.Service") #Need Admin do to this!
$ScheduleObj.connect($ComputerName)
[System.__ComObject]$TaskRootFolderObj = $ScheduleObj.GetFolder("\")
} Catch {
[string]$exceptionMessage = "$($_.Exception.Message) <code>($($_.ScriptStackTrace)</code>)"
[String]$message = "ERROR: Cannot connect to [Schedule.Service]. Are we running elevated? $exceptionMessage"
If ($ContinueOnError) {
Write-Log $message -Source ${CmdletName}
return $false #exit function
} else { Throw $message }
}
#TODO SomeDay: Retrieve TaskNamePath from TaskXmlContent Description's Last line is @@@MyTaskFolder\MyTaskName
Write-Log "TaskNamePath is [$TaskNamePath]" -Source ${CmdletName}
If ( $TaskNamePath -match '\\') { #Get $TaskName and $TaskFolderPath from TaskNamePath
[string]$TaskName = [System.IO.Path]::GetFileName($TaskNamePath)
[string]$TaskFolderPath = [System.IO.Path]::GetDirectoryName($TaskNamePath)
} else {
[string]$TaskName = $TaskNamePath
[string]$TaskFolderPath = “” #RootTaskFolder
}
}
Process {
#Remove ALL Tasks in ONE Task Folder (Except ROOT Task Folder)
If ( ($Remove) -and ($TaskName -eq "*") ) {
If ($TaskRootFolderObj.Path -eq $TaskFolderObj.Path ) {
[String]$message = "Will NOT delete all tasks in [ROOT Task Folder]. Allowing this would break too many things!"
If ($ContinueOnError) {
Write-log $Message -Source ${CmdletName}
return $false #exit function
} else { Throw "ERROR: $Message" }
}
Try {
[System.__ComObject]$TaskFolderObj = $ScheduleObj.GetFolder($TaskFolderPath)
} Catch {
[string]$exceptionMessage = "$($_.Exception.Message)] <code>($($_.ScriptStackTrace)</code>)"
[String]$message = "Task folder [$TaskFolderPath] does not exist. `nNothing to Delete. $exceptionMessage"
If ($ContinueOnError) {
Write-log $message -Source ${CmdletName}
return $True #exit function
} else { Throw "ERROR: $message" }
}
Write-Log "Deleting Task folder [$($TaskFolderObj.Path)] regardless of the number of tasks in the folder" -Source ${CmdletName}
Try {
[System.__ComObject]$AllTasks = $TaskFolderObj.GetTasks(1) #1= include hidden tasks too
} Catch {
[string]$exceptionMessage = "$($_.Exception.Message)] <code>($($_.ScriptStackTrace)</code>)"
[String]$message = "Unable to get Tasks in Task folder [$TaskFolderPath] $exceptionMessage"
If ($ContinueOnError) {
Write-log $message -Source ${CmdletName}
return $false #exit function
} else {
Throw "ERROR: $message"
}
}
[Int32]$TotalNumTasks=$AllTasks.count
Write-log "[$TaskFolderPath] has $TotalNumTasks task(s)" -Source ${CmdletName}
If ($TotalNumTasks -gt 0 ) {
ForEach ($Task in $AllTasks) {
Try {
$Task.Stop(0) #Just in case, should test if running first
Start-Sleep -Seconds 1
$TaskFolderObj.DeleteTask($Task.Name,0)
Write-Log ("Task [$($Task.Name)] was deleted") -Source ${CmdletName}
} Catch {
[String]$message = "Cannot delete task [$($Task.Name)]. Might not exist or stopped"
If ($ContinueOnError) {
Write-log $message -Source ${CmdletName}
} else { Throw "ERROR: $message" }
}
}
#Are they all gone?
[System.__ComObject]$AllTasks = $TaskFolderObj.GetTasks(1) #1= include hidden tasks too
If ($($AllTasks.Count) -ne 0) {
[string]$ErrMess = "ERROR: Not all tasks have been deleted from the task folder."
ForEach ($Task in $AllTasks) {
$ErrMess = $ErrMess + "`nTask [$($Task.Name)]"
}
If ($ContinueOnError) {
Write-log $ErrMess -Source ${CmdletName}
return $false #exit function
} else { Throw "ERROR: $ErrMess" }
} else {
#Write-log "INFO:No Tasks to delete in task folder[$SubFolderPath]." -Source ${CmdletName}
}
} else {
Write-Log "INFO: No tasks to delete in task folder [$SubFolderPath]." -Source ${CmdletName}
}
#Delete the Task Folder
if ($RemoveTaskFolderIfEmpty) {
#CAVEAT : you must use .DeleteFolder method with the PARENT of the task folder you want to delete
#CAVEAT2: I didn't bother to add code for subfolders b/c I didn't need it.
$SubFolderName=Split-Path -Path $TaskFolderObj.Path -Leaf
$ParentFolderPath=Split-Path -Path $TaskFolderObj.Path -Parent
Try {
[System.__ComObject]$ParentFolderObj = $ScheduleObj.GetFolder($ParentFolderPath)
} Catch {
[string]$exceptionMessage = "$($_.Exception.Message) <code>($($_.ScriptStackTrace)</code>)"
Throw "Task folder [${ParentFolderPath}] does not exist. `nNothing to Delete. $exceptionMessage"
Return $true #exit Function
}
#No need to check for left-over tasks. We did this already above
Try {
$ParentFolderObj.DeleteFolder($SubFolderName,$null)
Write-Log "Task folder [$SubFolderName] was deleted" -Source ${CmdletName}
return $true #exit Function
} Catch {
$exceptionMessage = "$($_.Exception.Message) <code>($($_.ScriptStackTrace)</code>)"
[String]$message = "Unable to delete Task folder [$SubFolderName] $exceptionMessage"
If ($ContinueOnError) {
Write-log $message -Source ${CmdletName}
return $false #exit Function
} else { Throw "ERROR: $message" }
}
} Else {
#Write-log "Not attempting to delete Task Folder [$($TaskFolderObj.Path)]" -Source ${CmdletName}
return $true #exit Function
}
}
#Remove ONE Task
If ( ($Remove) -and ($TaskName -ne "") ) {
Try {
[System.__ComObject]$TaskFolderObj = $ScheduleObj.GetFolder($TaskFolderPath)
} Catch {
[String]$message = "Task folder [${TaskFolderPath}] does not exist. `nNothing to Delete."
If ($ContinueOnError) {
Write-log $message -Source ${CmdletName}
return $true #exit Function
} else { Throw "ERROR: $message" }
}
Write-Log ("Task [$TaskName] will be removed") -Source ${CmdletName}
Try {
[System.__ComObject]$task=$TaskFolderObj.gettask($TaskName)
$Task.Stop(0) #Stop the task, Just in case
Start-Sleep -Seconds 1
$TaskFolderObj.DeleteTask($TaskName,0)
Write-Log ("Task [$TaskName] was deleted") -Source ${CmdletName}
} Catch {
[String]$message = "INFO:Cannot delete task [$TaskName]. It might not exist."
Write-log $message -Source ${CmdletName}
return $True #exit Function
}
#TODO: Check if the Task is still in $TaskFolderObj or not ( use gettasks() ?)
if ($RemoveTaskFolderIfEmpty) {
If ($TaskRootFolderObj.Path -eq $TaskFolderObj.Path ) {
[String]$message = "INFO: Cannot delete [ROOT task] folder."
If ($ContinueOnError) {
Write-log $message -Source ${CmdletName}
} else { Throw "ERROR: $message" }
} else {
Try {
#CAVEAT : you must use .DeleteFolder method with the PARENT of the task folder you want to delete
#CAVEAT2: I didn't bother to add code for subfolders b/c I didn't need it.
$SubFolderName=Split-Path -Path $TaskFolderObj.Path -Leaf
$ParentFolderPath=Split-Path -Path $TaskFolderObj.Path -Parent
Try {
[System.__ComObject]$ParentFolderObj = $ScheduleObj.GetFolder($ParentFolderPath)
} Catch {
[string]$exceptionMessage = "$($_.Exception.Message) <code>($($_.ScriptStackTrace)</code>)"
Throw "Cannot Get Task folder [${ParentFolderPath}] in order to delete $SubFolderName. $exceptionMessage"
Return $false #exit Function
}
#Is $TaskFolder empty?
[System.__ComObject]$AllTasks = $TaskFolderObj.GetTasks(1) #1= include hidden tasks too
If ($($AllTasks.Count) -eq 0) {
$ParentFolderObj.DeleteFolder($SubFolderName,$null)
Write-Log "Task folder [$SubFolderName] was deleted" -Source ${CmdletName}
} Else {
Write-Log "INFO: Cannot delete Task folder [$SubFolderName]. It still contains $($AllTasks.Count) task(s)." -Source ${CmdletName}
}
return $True #exit Function
} Catch {
$exceptionMessage = "$($_.Exception.Message) <code>($($_.ScriptStackTrace)</code>)"
[String]$message = "Unable to delete Task folder [$SubFolderName] $exceptionMessage"
If ($ContinueOnError) {
Write-log $message -Source ${CmdletName}
return $false #exit Function
} else { Throw "ERROR: $message" }
}
} #Else
} Else {
#Write-log "Not attempting to delete Task Folder [$($TaskFolderObj.Path)]" -Source ${CmdletName}
return $true #exit Function
}
}
#Create Task
If ($TaskName -eq "*") {
[String]$message = "Cannot create task named $TaskName"
If ($ContinueOnError) {
Write-log $message -Source ${CmdletName}
return $false #exit Function
} else { Throw "ERROR: $message" }
}
If (-not ( $TaskXmlContent) ) {
[String]$message = "Cannot create task without -TaskXmlContent parameter"
If ($ContinueOnError) {
Write-log $message -Source ${CmdletName}
return $false #exit Function
} else { Throw "ERROR: $message" }
}
If ($TaskFolderPath) { #Create TaskFolder if needed
Write-Log "Creating Task folder [${TaskFolderPath}] If needed." -Source ${CmdletName}
Try { $TaskRootFolderObj.CreateFolder($TaskFolderPath) } Catch { } #ignore already exists error
Try {
[System.__ComObject]$TaskFolderObj =$ScheduleObj.GetFolder($TaskFolderPath)
} Catch {
[String]$message = "Task folder [${TaskFolderPath}] does not exist. Cannot create task in non-existing task folder."
If ($ContinueOnError) {
Write-Log $message -Source ${CmdletName}
Return $false
} Else { Throw "ERROR: $message" }
}
} Else { #Task will be created in the "Root" Task Folder
[System.__ComObject]$TaskFolderObj = $TaskRootFolderObj
}
#Creating task (In sub folder if needed)
[System.__ComObject]$NewTask = $ScheduleObj.NewTask($null) #Create blank task
If ( $($TaskXmlContent.gettype().name) -eq "XmlDocument") {
$NewTask.XmlText = $TaskXmlContent.OuterXml -as [string] #load XmlText property
} ElseIf ( $($TaskXmlContent.gettype().name) -eq "String") {
$NewTask.XmlText = $TaskXmlContent #load XmlText property
} Else {
[String]$message = "-TaskXmlContent as [$($TaskXmlContent.gettype().name)] is not supported. Please cast as [XmlDocument] or [string]."
If ($ContinueOnError) {
Write-log $message -Source ${CmdletName}
return $false #exit Function
} else { Throw "ERROR: $message" }
}
#It can overwrite an existing task just fine provided that the task folder exists already
# but just in case...
Try {
Write-Log "Creating Task [$TaskName]..." -Source ${CmdletName}
$RegistrationResult=$TaskFolderObj.RegisterTaskDefinition($TaskName, $NewTask, 6, $TaskUser, $TaskPwd, 1, $null)
#Write-log "DEV: $RegistrationResult" -Source ${CmdletName}
} catch {
[string]$exceptionMessage = "$($_.Exception.Message) <code>($($_.ScriptStackTrace)</code>)"
[String]$message = "Unable to import task [$TaskName] $exceptionMessage"
If ($ContinueOnError) {
Write-log $message -Source ${CmdletName}
return $false
} Else { throw "ERROR: $message" }
}
#$RegistrationResult is a Massive block of text but not as [string]
#[String]$RegistrationResultString = Out-String -InputObject $RegistrationResult
#return $RegistrationResultString
Return $true
}
End {
Write-FunctionHeaderOrFooter -CmdletName ${CmdletName} -Footer
}
} # Set-ScheduledTask