Some of our application servers have scheduled tasks to deploy a staged build of our application to the current app server. In the past when I wanted to manually run the task I would do one of the following:

  • Remote desktop into the app server and run the task from Scheduled Tasks
  • Use Computer Management on my local machine to connect to the remote computer and run scheduled task from there
  • Use a command prompt or PowerShell console with schtasks

The first two GUI options took too long and the last worked but I could never remember the syntax and it was still a bit manual. So I decided I would take a quick stab at a PowerShell script that would start a remote task and wait on it to complete.

The below function is my first pass attempt. I am sure you PowerShell experts can tell me a better way to do this and in 75% less code but for now this works though it is not foolproof.

function WaitOnScheduledTask($server = $(throw "Server is required."), $task = $(throw "Task is required."), $maxSeconds = 300)
{
    $startTime = get-date
    $initialDelay = 3
    $intervalDelay = 5
    
    Write-Output "Starting task '$task' on '$server'. Please wait..."
    schtasks /run /s $server /TN $task
    # wait a tick before checking the first time, otherwise it may still be at ready, never transitioned to running
    Write-Output "One moment..."
    start-sleep -s $initialDelay
    $timeout = $false
    while ($true)
    {
        $ts = New-TimeSpan $startTime $(get-date)
        
        # this whole csv thing is hacky but one workaround I found for server 2003
        $tempFile = Join-Path $env:temp "SchTasksTemp.csv"
        schtasks /Query /FO CSV /s $server /TN $task /v > $tempFile
        $taskData = Import-Csv $tempFile 
        $status = $taskData.Status
        
        if($status.tostring() -eq "Running")
        {
            $status = ((get-date).ToString("hh:MM:ss tt") + " Still running '$task' on '$server'...")
            Write-Progress -activity $task -status $status -percentComplete -1 #-currentOperation "Waiting for completion status"
            Write-Output $status
        }
        else
        {
            break
        }
        start-sleep -s $intervalDelay   
        
        if ($ts.TotalSeconds -gt $maxSeconds)
        {
            $timeout = $true
            Write-Output "Taking longer than max wait time of $maxSeconds seconds, giving up all hope. Task execution continues but I'm peacing out."
            break
        }
    }
    if (-not $timeout)
    {
        $ts = New-TimeSpan $startTime $(get-date)
        "Scheduled task '{0}' on '{1}' complete in {2:###} seconds" -f $task, $server, $ts.TotalSeconds
    }
}

With the function in place a couple convenience caller functions save a little typing:

function DeployAppToDev
{
    WaitOnScheduledTask "dev-server" "Copy Latest Build" 120
}
function DeployAppToTest
{
    WaitOnScheduledTask "test-server" "Deploy Main Codeline" 120
}

Incorporating the code in my PowerShell profile, creating a SlickRun keyword and Windows shortcuts further eased the convenience of invoking the script code.

Sample output follows:

H:\> DeployAppToDev
Starting task 'Copy Latest Build' on 'dev-server'. Please wait...
SUCCESS: Attempted to run the scheduled task "Copy Latest Build".
One moment...
09:10:42 PM Still running 'Copy Latest Build' on 'dev-server'...
09:10:48 PM Still running 'Copy Latest Build' on 'dev-server'...
09:10:53 PM Still running 'Copy Latest Build' on 'dev-server'...
09:10:58 PM Still running 'Copy Latest Build' on 'dev-server'...
09:10:03 PM Still running 'Copy Latest Build' on 'dev-server'...
09:10:09 PM Still running 'Copy Latest Build' on 'dev-server'...
09:10:14 PM Still running 'Copy Latest Build' on 'dev-server'...
09:10:19 PM Still running 'Copy Latest Build' on 'dev-server'...
09:10:24 PM Still running 'Copy Latest Build' on 'dev-server'...
09:10:29 PM Still running 'Copy Latest Build' on 'dev-server'...
09:10:35 PM Still running 'Copy Latest Build' on 'dev-server'...
Scheduled task 'Copy Latest Build' on 'dev-server' complete in 61 seconds

There are various other options available for this kind of thing such as using the Task Scheduler API.

2 Thoughts to “Start Scheduled Task and Wait On Completion with PowerShell”

  1. Jesse

    Along the same lines, but I didn't have much need for as much progress output and error checking. You don't have to write to and read from a file, keep things in memory!


    while ((& schtasks.exe /query /s $Server /TN "$Task" /FO CSV | ConvertFrom-Csv | select -expandproperty Status -first 1) -eq "Ready") {
    write-host "$((get-date).tostring())`tTas $Task on $Server still running."
    start-sleep 60
    }

  2. Jeff

    Nice, this is just what I was looking for. Thanks for the time and effort put into this.

Comments are closed.