Programmatically scheduling PowerShell scripts in Task Scheduler

In the article Calling PowerShell scripts from Task Scheduler I described how to call a PowerShell script from the scheduled task. Here I go a step further and let the script "install" itself by creating a schedule in Task Scheduler.

The following function uses the command-line tool schtasks.exe to create a schedule that runs Monday to Friday at 8:00 a.m.

function CreateSchedule() {  
    $logFile = join-path (pwd) "Scheduling.log" #use absolute path in the scheduled task
    $taskName = "Daily Job"
    $thisScript = $PSCommandPath
    $callParams = @("/Create", 
                    "/TN", $taskName, 
                    "/SC", "weekly", 
                    "/D", "MON,TUE,WED,THU,FRI", 
                    "/ST", "08:00", 
                    "/TR", ("powershell.exe -File `"$thisScript`" -ConsoleOutputFile `"$logFile`"" -replace '"', '\"'), 
                    "/F", #force
                    "/RU", "system") #run under the system account

    Log "schtasks.exe", $callParams
    $o = & "schtasks.exe" $callParams
    Log $o
}

Note the escaping of quotation marks in the task command: "powershell.exe -File `"$thisScript`" -ConsoleOutputFile `"$logFile`"" -replace '"', '\"' We use PowerShell to call a command to create a schedule that calls another command. Each of the steps requires its own escaping of quotation marks.

If we can install the script, we should be able to uninstall it too:

function DeleteSchedule() {  
    $taskName = "Daily Job"
    $callParams = @("/Delete", 
                    "/TN", $taskName, 
                    "/F") #force

    Log "schtasks.exe", $callParams
    $o = & "schtasks.exe" $callParams
    Log $o
}

Example

Below is a full example of a script including the scaffolding of parameters declaration and main(). The function DoTheJob() would do the actual work. The parameter ConsoleOutputFile and method Log() are described in the article Calling PowerShell scripts from Task Scheduler.

[CmdletBinding(DefaultParametersetName="run")]
param(  
    [Parameter(ParameterSetName='install')] 
    [switch]$Install, 
    [Parameter(ParameterSetName='uninstall')]
    [switch]$Uninstall, 
    #Path to the text file to which the standard output of the script will be redirected. If omitted, the output is printed to the host.
    [string]$ConsoleOutputFile
)

function Main() {  
    if($Install) {
        DeleteSchedule
        CreateSchedule
    } elseif($Uninstall) {
        DeleteSchedule
    } else {
        DoTheJob
    }
}

function Log($msg) {  
    if(($ConsoleOutputFile -eq $null) -or ($ConsoleOutputFile -eq "")) {
        Write-Host $msg
    } else {
        if(-not (Test-Path $ConsoleOutputFile)) {
            New-Item $ConsoleOutputFile -ItemType file -Force #creates the file including any necessary directories
        }
        Add-Content $ConsoleOutputFile $msg
    }
}

function DoTheJob() {  
        $result = dir #any action that must be scheduled
        Log $result
}

function CreateSchedule() {  
    $logFile = join-path (pwd) "Scheduling.log" #use absolute path in the scheduled task
    $taskName = "Daily Job"
    $thisScript = $PSCommandPath
    $callParams = @("/Create", 
                    "/TN", $taskName, 
                    "/SC", "weekly", 
                    "/D", "MON,TUE,WED,THU,FRI", 
                    "/ST", "08:00", 
                    "/TR", ("powershell.exe -File `"$thisScript`" -ConsoleOutputFile `"$logFile`"" -replace '"', '\"'), 
                    "/F", #force
                    "/RU", "system") #run under the system account

    Log "schtasks.exe", $callParams
    $o = & "schtasks.exe" $callParams
    Log $o
}

function DeleteSchedule() {  
    $taskName = "Daily Job"
    $callParams = @("/Delete", 
                    "/TN", $taskName, 
                    "/F") #force

    Log "schtasks.exe", $callParams
    $o = & "schtasks.exe" $callParams
    Log $o
}

Main #run the script