Today, I’m going to describe what Windows PowerShell Workflow is, how it differs from PowerShell functions, and why you should care.
Latest posts by Timothy Warner (see all)

Windows PowerShell workflow

Windows PowerShell Workflow

What a workflow is

From the broadest possible perspective, a workflow is (according to Google) a sequence of industrial or administrative processes through which a piece of work passes from initiation to completion.

Microsoft built a workflow engine named, appropriately enough, Windows Workflow Foundation (WF), which allows .NET applications to offload workflow processing. The offloading piece is important because some long-running tasks can consume many CPU cycles.

Windows PowerShell v3 and later connects to WF via its own internal PSWorkflow core module. Specifically, you use the Workflow data structure to define one or more activities that have the following attributes:

  • Can affect one or more machines either in sequence or in parallel
  • Can survive power interruptions and system restarts
  • Can execute tasks in a specified order
  • Can be paused and resumed at the administrator’s discretion

When you run a PowerShell workflow, your activities are translated behind the scenes into Extensible Application Markup Language (XAML, pronounced ZAM-uhl) and handed off to WF for processing.

Here’s the requisite “Hello World” example of a PowerShell Workflow script:

Workflow Get-Hello {
	Write-Output –InputObject 'Hello world!'

In a nutshell, here is PowerShell Workflow’s primary use case: create workflows for long-running, potentially complex jobs that require persistence and possible administrator intervention.

From what I’ve seen and experienced in the industry, PowerShell Workflow is particularly suited to larger-scale provisioning and deprovisioning tasks. For instance, you can use Workflow to deploy a VM pod for a newly hired developer in your company.

Workflow scripts vs. PowerShell functions

At first blush, PowerShell Workflow syntax appears to mirror that of PowerShell functions. Consider the following simple example:

function Get-Hello2 {
          Write-Host 'Hello world!' -BackgroundColor Yellow -ForegroundColor Black

You call the workflow and function the same way:


However, if you try to make the Get-Hello2 function a workflow, PowerShell will throw errors. Why? Because the workflow isn’t native PowerShell but a “wrapper” around the Windows Workflow Foundation.

In fact, PowerShell Workflow has its own set of dedicated keywords:

That said, you can include an InlineScript expression to run “pure” PowerShell code as-is:

Workflow Get-Hello {
     InlineScript { Write-Host 'Hello world!' -BackgroundColor Yellow -         ForegroundColor Black }

You should read the workflow-related PowerShell conceptual help to learn more about this:

Get-Help -Name about_*workflow* | Select-Object -Property Name, Synopsis
 Name                           Synopsis
 ----                           --------
 about_Checkpoint-Workflow      Describes the Checkpoint-Workflow...
 about_Suspend-Workflow         Describes the Suspend-Workflow...
 about_WorkflowCommonParameters This topic describes the parameters...
 about_Workflows                Provides a brief introduction...

A sample workflow

Perhaps the best way to “sink our teeth” into PowerShell Workflow is to create a representative example. Imagine we have three Windows Server 2012 R2 computers:

  • dc: administrative server from which we’ll run the workflow
  • adfs1: member server
  • mem2: member server

It’s not insignificant to note that we create, run, and manage the workflow from a separate computer. You can even use your Windows 8.1 administrative workstation. By combining Windows PowerShell WS-Man remoting and Windows Workflow Foundation, you have a lot of automation and power at your disposal.

We’ll write a workflow named Get-SysInfo that performs the following (admittedly arbitrary) actions:

  • Ping one or more target hosts to determine their availability and write to an output file.
  • Retrieve WMI details on the target hosts and write to an output file.
  • Checkpoint the workflow.
  • Force a restart of each target host.
  • Gather BIOS details on the target hosts post-restart.

My workflow activities are so “all over the place” because I want to show you as much functionality as I can in a limited space. Without further ado, let me show you the code and then explain line by line what’s happening:

Workflow Get-Sysinfo {
    param( [string[]]$h)
    Parallel {
    Test-Connection -ComputerName $h -Count 1 | Out-File -FilePath 'C:\tmp\ping.txt' -Append
    Get-CimInstance -PSComputerName $h -ClassName Win32_OperatingSystem | Out-File -FilePath 'C:\tmp\os.txt' -Append


    foreach -parallel ($h1 in $h) {
        #Restart-Computer -Wait -Force -PSComputerName $h1
        Get-CimInstance -PSComputerName $h -ClassName Win32_BIOS | Out-File -FilePath 'C:\tmp\bios.txt' -Append

Get-Sysinfo -h mic8

1: We name our workflows according to PowerShell best practice, using the ApprovedVerb-SingularNoun format.

2: We define a string array variable so we can pass one or more computer names to the workflow.

4: Any activities we place in a Parallel block are executed at the same time.

5-6: You’ll have to experiment to determine which commands are or are not acceptable in a workflow. Generally, any command that requires manual intervention isn’t allowed.

9: This is how we can place checkpoints in a workflow. We can suspend and resume workflows at checkpoints; this makes your workflows highly resilient.

11: Another way to do parallel execution is the foreach -Parallel construction. Here, we iterate through a collection (in this case, computers) synchronously.

12: The -Wait switch parameter is important here so we can keep the workflow script active while the target machines complete the reboot and automatically resume the workflow.

17: Note that we call a script workflow in exactly the same way that we run PowerShell simple and advanced functions.

For grins, you can view the underlying workflow XAML code by running Get-Command:

Get-Command –Name Get-SysInfo | Format-List *

More on checkpoints

The idea behind workflow checkpoints is that we may want to prevent re-running an entire workflow if our script bombs out partway through. In addition to placing manual checkpoints in your workflow by using Checkpoint-Workflow, you can also instruct PowerShell to “automagically” take a checkpoint after each activity by invoking the PSPersist workflow common parameter:

Get-SysInfo –PSPersist $True

A checkpointed workflow persists on the hard drive (specifically, in the home folder of the user who is currently logged on) of the computer that’s orchestrating the workflow.

The other “gotcha” with checkpointed workflows is that they need to be started as a job; that’s what allows you to resume a suspended workflow job later:

Get-SysInfo –PSPersist $True –AsJob

We then call Get-Job, Receive-Job, and Resume-Job as we do normally.

More on parallel vs. serial execution

We saw that we can instruct Windows Workflow Foundation to run our workflow activities in parallel either by using the Parallel block or the foreach -Parallel construction.

Curiously, to run activities in a specified sequence, we need to place a sequence block within a Parallel block:

workflow start-testworkflow {
          Parallel {
                   Sequence {
                            Install-WindowsFeature –Name Web-Server –IncludeManagementTools
                            Get-Service –Name W3SVC 

More on workflow syntax

Recall that a PowerShell workflow is simply an IT administrator-friendly way to access Windows Workflow Foundation. If we were .NET developers, we’d create our XAML by using Visual Studio.

Therefore, we’re limited in the kinds of PowerShell code we can add to a workflow. Here are some general guidelines:

  • Spell out all your cmdlet calls (no aliases).
  • Use named parameters (no positional parameters).
  • Avoid any commands that reference user interaction (workflows are meant to run independently).

You can hack around the workflow syntax restriction by using an InlineScript block. Here, the workflow passes your PowerShell script commands as a workflow activity on its own and with no further comment:

workflow Start-TestWorkflow2 {
         InlineScript {
                \\mem2\scripts\Install-VMs.ps1 -All

Workflow is not an easy topic to master, that’s for sure. Nevertheless, I hope that I was able to get you a bit clearer on the subject. Thanks for reading!


Leave a reply

Please enclose code in pre tags: <pre></pre>

Your email address will not be published. Required fields are marked *


© 4sysops 2006 - 2023


Please ask IT administration questions in the forums. Any other messages are welcome.


Log in with your credentials


Forgot your details?

Create Account