Using the InvokePowerShell Activity
This topic applies to Windows Workflow Foundation 4 (WF4).
The InvokePowerShell sample demonstrates how to invoke Windows PowerShell commands using the InvokePowerShell
activity.
Demonstrates
Simple innovation of Windows PowerShell commands.
Retrieve values from the Windows PowerShell output pipeline and store them in workflow variables.
Pass data into windows PowerShell as input pipeline for an executing command.
Discussion
This sample contains the following three projects.
Project Name | Description | Main Files |
---|---|---|
CodedClient |
A sample client application that uses the PowerShell activity. |
|
DesignerClient |
A set of custom activities that contain the |
|
PowerShell |
The |
Activity Files
Designer files:
|
The client projects are discussed first, as it is easier to understand the internal functionality of the PowerShell activity once its use is understood.
Using This Sample
The following sections describe how to use the three projects in the sample.
Using the Coded Client Project
The sample client programmatically creates a sequence activity, which contains examples of several different methods of using the InvokePowerShell
activity. The first invocation launches Notepad.
new InvokePowerShell()
{
CommandText = "notepad"
},
The second invocation gets a list of the processes running on the local machine.
new InvokePowerShell<Process>()
{
CommandText = "Get-Process",
Output = processes1,
},
Output
is the variable used to store the output of the command.
The next call demonstrates how to run a post-processing step on each individual output of the PowerShell invocation. InitializationAction
is set to the function that outputs a string representation for each process. The collection of these strings is returned in the Output
variable by the InvokePowerShell<string>
activity.
The succeeding InvokePowerShell
calls demonstrate passing data into the activity and getting outputs and errors out.
Using the Designer Client Project
The DesignerClient project consists of a set of custom activities, almost all of which are built containing the InvokePowerShell
activity. Most of the activities call the non-generic version of the InvokePowerShell
activity, and do not expect a return value. Other activities use the generic version of the InvokePowerShell
activity, and use the InitializationAction
argument to post-process the results.
Using the PowerShell Project
The main action of the activity takes place in the ExecutePowerShell
class. Because the execution of PowerShell commands should not block the main workflow thread, the activity is created to be an asynchronous activity by inheriting from the AsyncCodeActivity class.
The BeginExecute method is called by the workflow runtime to begin running the activity. It starts by calling PowerShell APIs to create a PowerShell pipeline.
runspace = RunspaceFactory.CreateRunspace();
runspace.Open();
pipeline = runspace.CreatePipeline();
It then creates a PowerShell command and populates it with parameters and variables.
Command cmd = new Command(this.CommandText, this.IsScript);
// loop over parameters and run: cmd.Parameters.Add(...)
// loop over variables and run: runspace.SessionStateProxy.SetVariable(...)
pipeline.Commands.Add(cmd);
The inputs piped in are also sent to the pipeline at this point. Finally, the pipeline is wrapped in a PipelineInvokerAsyncResult
object and returned. The PipelineInvokerAsyncResult
object registers a listener and invokes the pipeline.
pipeline.InvokeAsync();
When execution finishes, output and errors are stored within the same PipelineInvokerAsyncResult
object, and control is handed back to the workflow runtime by calling the callback method originally passed to BeginExecute.
At the end of the method's execution, the workflow runtime calls the activity’s EndExecute method.
The InvokePowerShell
class wraps the ExecutePowerShellCommand
class and creates two versions of the activity; a generic version and a non-generic version. The non-generic version returns the output of the PowerShell execution directly, whereas the generic version transforms the individual results to the generic type.
The generic version of the activity is implemented as a sequential workflow that calls ExecutePowerShellCommand
and post-processes its results. For each element in the result collection, the post-processing step calls InitializationAction
if it is set. Otherwise, it does a simple cast.
new ForEach<PSObject>
{
Values = psObjects,
Body = new ActivityAction<PSObject>
{
Argument = psObject,
Handler = new Sequence
{
Activities =
{
new If
{
Condition = // Is InitializationAction set?
Then = new InvokeFunc<PSObject, TResult>
{
Argument = psObject,
Result = outputObject,
Func = this.InitializationAction
},
Else = new Assign<TResult>
{
To = outputObject,
Value = new InArgument<TResult>(ctx => (TResult) psObject.Get(ctx).BaseObject),
}
},
new AddToCollection<TResult>
{
Collection = outputObjects,
Item = outputObject
},
}
}
}
},
For each of the two InvokePowerShell
activities (generic, and non-generic), a designer was created. InvokePowerShellDesigner.xaml and its associated .cs file define the appearance in Workflow Designer for the non-generic version of the InvokePowerShell
activity. Inside InvokePowerShellDesigner.xaml, a DockPanel is used to represent the graphical interface.
<DockPanel x:Uid="DockPanel_1" LastChildFill="True">
<TextBlock x:Uid="TextBlock_1" Text="CommandText" />
<TextBox x:Uid="TextBox_1" Text="{Binding Path=ModelItem.CommandText, Mode=TwoWay}"
TextWrapping="WrapWithOverflow" AcceptsReturn="True" MinLines="4" MaxLines="4"
Background="{x:Null}" Margin="3" />
</DockPanel>
Note that the Text
property of the text box is a two-way binding that ensures that the value of the activity’s CommandText
property is equivalent to the value input into the designer.
GenericInvokePowerShellDesigner.xaml and its associated .cs file define the graphical interface for the generic InvokePowerShell
activity. The designer is slightly more complicated because it allows users to set an InitializationAction
. The key element is the usage of WorkflowItemPresenter to allow drag and drop of child activities onto the InvokePowerShell
designer surface.
<sap:WorkflowItemPresenter x:Uid="sap:WorkflowItemPresenter_1" Margin="0,10,0,10"
HintText="Drop Activities Here"
AllowedItemType="{x:Type sa:Activity}"
Item="{Binding Path=ModelItem.InitializationAction.Handler, Mode=TwoWay}"
Grid.Row="1" Grid.Column="1" />
The designer customization does not stop with the .xaml files that define the appearance of the activity on the design canvas. The dialog boxes used to display the parameters of the activity can also be customized. These parameters and PowerShell variables affect the behavior of PowerShell commands. The activity exposes them as Dictionary types. ArgumentDictionaryEditor.cs, PropertyEditorResources.xaml and PropertyEditorResources.cs define the dialog box that allows you to edit these types.
To set up, build, and run the sample
You must install Windows PowerShell to run this sample. Windows PowerShell can be installed from this location: Windows PowerShell.
To run the coded client
Open PowerShell.sln using Visual Studio 2010.
Right-click the solution and build it.
Right-click the CodedClient project and select Set as Startup Project.
Press CTRL+F5 to run the application.
To run the designer client
Open PowerShell.sln using Visual Studio 2010.
Right-click the solution and build it.
Right-click the DesignerClient project and select Set as Startup Project.
Press CTRL+F5 to run the application.
Known Issues
If referencing the
InvokePowerShell
activity assembly or project from another project results in a build error, you may need to manually add the<SpecificVersion>True</SpecificVersion>
element to the .csproj file of the new project under the line that referencesInvokePowerShell
.If Windows PowerShell is not installed, the following error message is displayed in Visual Studio as soon as you add an
InvokePowerShell
activity onto a workflow:Workflow Designer encountered problems with your document. Could not load file or assembly ‘System.Management.Automation’ ... or one of its dependencies. The system cannot find the file specified.
In Windows PowerShell 2.0, programmatically calling
$input.MoveNext()
fails and scripts using$input.MoveNext()
produce unintended errors and results. To work around this issue, consider using the PowerShell verbforeach
instead of callingMoveNext()
when iterating an array.
Note: |
---|
The samples may already be installed on your computer. Check for the following (default) directory before continuing.
<InstallDrive>:\WF_WCF_Samples
If this directory does not exist, go to Windows Communication Foundation (WCF) and Windows Workflow Foundation (WF) Samples for .NET Framework 4 to download all Windows Communication Foundation (WCF) and WF samples. This sample is located in the following directory.
<InstallDrive>:\WF_WCF_Samples\WF\Scenario\ActivityLibrary\PowerShell
|