oops

How to handle reboot and resume or continue in PowerShell with PowerShell Workflow

While developing a Azure Resource Manager Template with a virtual machine resource and custom script extension, I got the problem, that I have to install a software that needs a reboot and after the reboot, the script should continue to configure these software and install other applications.

The recommended way with custom script extensions is, to have one script, that acts like a start script calling other scripts doing the work. This sounds like a workflow. So I thought I can use PowerShell Workflow to handle this and I gave it a try. With PowerShell Workflow we have all mechanism to handle reboot and resume or continue in a PowerShell script, but it’s anyway a little bit tricky, because we have to use a scheduled task which will be triggered “At startup”.

It was not successful to use it in the Azure Resource Manager virtual machine extension because of other reasons (finally I solved it with Windows PowerShell Desired State Configuration (DSC)), but in general is PowerShell Workflow a fine technology for task which needs a state, because they might be suspended and resumed and could run in parallel. So I will share my experience with you.

First, lets think about the workflow and the single steps called “Activities”. What must be known in advance is, like I wrote already above, is, that PowerShell Workflow is designed to run activities in parallel and that each activity has it’s own workspace. That means, that results i.e. returned to variables cannot be used from the next activity. Each PowerShell command that runs within a workflow is a single, standalone activity. To run activities parallel, the parallel{} keyword must be used and when activites inside the parallel block should run in a defined order the sequence{} keyword must be defined.

For our purpose, following script snipped can be used:

Workflow New-ServerSetup
{
    parallel {
    "1. activity?"
    "2. activity?"
    "3. activity?"
    "4. activity?"
    "..."
    }
    Restart-Computer -Wait 
    "Last activity"
    "or more activities..."
}
# Run the workflow
New-ServerSetup

When this would be executed, the "Last activity" and "or more activities..." would not be processed, because with the Restart-Computer -Wait activity, the New-ServerSetup job will be suspended and will stay in that state after reboot. This can be checked after the server rebooted with

Get-Job
Check status of current jobs

Check status of current jobs

To manually resume the Job, just type

# In our case the job Id is 3. Check if you put the right Id
Resume-Job -Id 3
PowerShell Workflow Suspended, Running, Completed

PowerShell Workflow Suspended, Running, Completed

But, of course, we don’t have the possibility to start the job manually, when it’s executed from the Azure Resource Manager Template virtual machine extension. So, we have to define a scheduled task, resuming the job “at startup”:

Workflow New-ServerSetup
{
    "First activity"
    "Second activity"
    "..."
    Restart-Computer -Wait 
    "Last activity"
    "or more activities..."
    Unregister-ScheduledJob -Name NewServerSetupResume
}
# -------------------------------------------------------------------------
# Use the New-JobTrigger cmdlet to create an "At startup" trigger
# to resume the suspended job.
# Replace <Password> with a password of a administrator
# on the local machine. 
# -------------------------------------------------------------------------
$adm = "Administrator"
$pwd = ConvertTo-SecureString -String "<Password>" -AsPlainText -Force
$cred = New-Object System.Management.Automation.PSCredential($adm, $pwd)
$AtStartup = New-JobTrigger -AtStartup
Register-ScheduledJob -Name NewServerSetupResume `
                      -Credential $cred `
                      -Trigger $AtStartup `
                      -ScriptBlock {Import-Module PSWorkflow; `
                          Get-Job -Name NewSrvSetup -State Suspended `
                          | Resume-Job}

# Run the workflow. It is suspended when the computer restarts.
# We give a defined name for the job, to be able to use the name
# in the scheduled task, otherwise the name would be "Job<n>"
New-ServerSetup -JobName NewSrvSetup

To find out more about Windows PowerShell Workflows use:

Get-Help about_workflows