I’ve been fooling a bit with the new Windows PowerShell-related activities included in Windows Workflow Foundation 4.0 alongside the Visual Studio 2010 Beta 1 release. It’s pretty cool seeing PowerShell being directly supported right out of the box!
There are two different activities: InvokePowerShell
and InvokePowerShell
. The difference (as far as I can see) is that the latter allows you to capture the output of the PowerShell command being executed (that is, the results from the pipeline). I can understand why both variants might be needed, but not thrilled about the naming. At the least, it’s not immediately obvious what the difference between the two is, so it’s going to be confusing.
Using InvokePowerShell
I struggled a bit understanding how the InvokePowerShell
activity was meant to be used. In particular, I didn’t grasp immediately the significance of the IsScript
property of the activity:
If I understand it correctly, here’s the key difference: If IsScript
is not checked (the default), then CommandText
cannot be an arbitrary PowerShell expression. Instead it must be the name of the PowerShell cmdlet to invoke, but you cannot add any extra information there (such as the value of a parameter). By contrast, if IsScript
is checked, then CommandText
can be a list of (more or less) arbitrary PowerShell expressions, including variable declarations and all.
I believe that for a lot of people, enabling IsScript
will make the activity behave in a more natural way.
There are a few more interesting details about the PowerShell activities:
Passing Data into PowerShell
There are two ways to pass data into the PowerShell script to execute:
- If you’re executing a cmdlet, then the Parameters collection can be used to provide input arguments to the cmdlet call. For example, if
CommandText
is“Get-Process”
, then you can add aName
parameter to theParameters
collection with the value set to an expression returning a string, and it will be the same as if you had invoked“Get-Process -Name
. I’m not 100% sure yet, but it would seem like this would only really apply if” IsScript
is unchecked (otherwise, it’s not very clear what you’re passing the parameters to). - You can have WF push variables into the PowerShell runspace before the invocation. This is what the
PowerShellVariables
property is for. If you add a variable there callError
, then the PowerShell expression inCommandText
can reference the value by using$Error
. Again, this would appear to be mostly useful whenIsScript
is checked.
Based on this, one might suspect that the Parameters
and PowerShellVariables
properties are basically there to support both modes of operation of the activity: One when invoking a cmdlet directly and one when executing a more free-form script. Is this good? I’m not sure yet. I can see a sort of logic to both ways of using the InvokePowerShell
activity, but it seems a bit complex and potentially confusing. I sure know I was confused the first time I tried to use it.
Pipeline Input
It should be obvious by now that I wasn’t exactly right when I said there were two ways to pass data into the PowerShell script. There’s a third (main?) one: Provide an object (or collection of objects) as the pipeline input, which is what the Input
property of the activity is for.
It should be fairly obvious how to use it: If the command is a cmdlet, then the input will be feed just like if you had piped something to it in PowerShell. You might expect that if the command is a free-form script, then you’d be able to access it the inputs $_
; however, I haven’t been successful in that so not sure if it’s supported.
Pipeline Output
Handling pipeline output is only done in the InvokePowerShell
activity, and it’s basically very simple: When adding the activity to your workflow, the designer will ask you to bind T
to a type, and that’s what you expect your PowerShell command to return. I can imagine that in most cases, this should be some sort of collection (like IEnumerable
) since most PowerShell commands will return multiple objects.
Then, just bind the Output
property of the activity to a variable in your workflow that will receive the pipeline output after the activity executes.
Pipeline Errors
Errors resulting from executing the InvokePowerShell
activity can surface in one of two ways:
- Certain errors, such as trying to invoke a malformed expression or a non-existent cmdlet will result in an exception being thrown and the Workflow faulting, unless you have the activity inside try/catch blocks.
- Anything that gets written by the command to the error stream (through the Write-Error cmdlet) will get exposed through the
Errors
property of theInvokePowerShell
activity. To handle those, you’ll want to create a new variable in your workflow (of typeCollection
or justIEnumerable
), and then bind it to theErrors
property. Now you should be able to simply iterate over your variable right afterInvokePowerShell
finishes executing.
Designer Support
I’m not totally happy with the designer support for the PowerShell activities. Besides my general complaints about the Workflow Designer (a subject for another blog post), there’s something that really bugs me: There’s no proper editor support for entering the CommandText
in the activity.
Sure, you can attempt to write your script directly into the text editor in the activity designer, but that’s way too small for anything more complex than, say, calling Get-Process
. You could also attempt to use the property window, but that’s even worse because all it has is a single-line text box. At the very least, it needs to popup into a multi-line text editor window.