{"id":2321,"date":"2012-06-19T15:09:12","date_gmt":"2012-06-19T15:09:12","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/powershell\/2012\/06\/19\/high-level-architecture-of-windows-powershell-workflow-part-2\/"},"modified":"2019-02-18T13:05:38","modified_gmt":"2019-02-18T20:05:38","slug":"high-level-architecture-of-windows-powershell-workflow-part-2","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/powershell\/high-level-architecture-of-windows-powershell-workflow-part-2\/","title":{"rendered":"High Level Architecture of Windows PowerShell Workflow (Part 2)"},"content":{"rendered":"<p align=\"justify\"><font size=\"3\">This is the second part of our post on the high level architecture of Windows PowerShell Workflow. <\/font><a href=\"http:\/\/blogs.msdn.com\/b\/powershell\/archive\/2012\/06\/15\/high-level-architecture-of-windows-powershell-workflow-part-1.aspx\"><font size=\"3\">Part 1 of this blog post<\/font><\/a><font size=\"3\"> provided an overview of the architecture and its various components. This post will go into more detail on the various subcomponents and provide some insight into the internals of Windows PowerShell Workflow. The component diagram for the PowerShell Workflow Executive is repeated (from part 1) for reference<\/font><\/p>\n<p> <font size=\"3\"><\/font>  <\/p>\n<p><a href=\"http:\/\/devblogs.microsoft.com\/powershell\/wp-content\/uploads\/sites\/30\/2012\/06\/0537.image_thumb_0116A054.png\"><img decoding=\"async\" src=\"http:\/\/devblogs.microsoft.com\/powershell\/wp-content\/uploads\/sites\/30\/2012\/06\/0537.image_thumb_0116A054.png\" alt=\"component diagram\" width=\"712\" height=\"373\" class=\"aligncenter size-full wp-image-14478\" srcset=\"https:\/\/devblogs.microsoft.com\/powershell\/wp-content\/uploads\/sites\/30\/2012\/06\/0537.image_thumb_0116A054.png 712w, https:\/\/devblogs.microsoft.com\/powershell\/wp-content\/uploads\/sites\/30\/2012\/06\/0537.image_thumb_0116A054-300x157.png 300w\" sizes=\"(max-width: 712px) 100vw, 712px\" \/><\/a><\/p>\n<p> <font size=\"3\"><\/font>  <\/p>\n<p><font size=\"3\">Each of the components in the diagram is discussed in detail below.<\/font><\/p>\n<p> <font size=\"3\"><\/font>  <\/p>\n<h2>1. Workflow Compilation, Caching and Validation<\/h2>\n<p> <font size=\"3\"><\/font>  <\/p>\n<p align=\"justify\"><a href=\"http:\/\/msdn.microsoft.com\/en-us\/netframework\/aa663328.aspx\"><font size=\"3\">Windows Workflow Foundation<\/font><\/a><font size=\"3\"> deals with and understands Activity objects. An executable workflow is simply a collection of activity objects. Each step in the workflow corresponds to an activity in the collection. The activity objects in a workflow can be programmatically constructed or constructed from their eXtensible Application Markup Language (XAML) definition. In Windows PowerShell workflow, we use the XAML definition of an activity. .<\/font><\/p>\n<p> <font size=\"3\"><\/font>  <\/p>\n<h2>1.1 Compilation and Caching<font size=\"3\"><\/font><\/h2>\n<p align=\"justify\"><font size=\"3\">The interaction begins when a user imports a XAML file into a Windows PowerShell session &#8212; either a console session or the PowerShell Workflow Configuration. XAML files are treated like modules in PowerShell, so they are imported by using the <font size=\"4\" face=\"Courier New\">Import-Module<\/font> cmdlet. <\/font><\/p>\n<p> <font size=\"3\"><\/font>  <\/p>\n<p><font size=\"3\">Here is what happens when a XAML file is imported:<\/font><\/p>\n<p> <font size=\"3\"><\/font>  <\/p>\n<p align=\"justify\"><font size=\"3\">1. The XAML file contents (and any dependencies) are read and compiled into an executable workflow using the <font size=\"4\" face=\"Courier New\">ActivityXamlServices<\/font> class.<\/font><\/p>\n<p> <font size=\"3\"><\/font>  <\/p>\n<p align=\"justify\"><font size=\"3\">2. The Activity object, the XAML definition, and any dependency information are stored in a process-wide cache. <\/font><\/p>\n<p align=\"justify\"><i><font size=\"3\">We do this because compiling a XAML definition into an executable workflow is a lengthy operation that we don&#8217;t want to repeat. <\/font><\/i><\/p>\n<p align=\"justify\"><font size=\"3\">3. The parameters of the workflow are extracted and a PowerShell function is synthesized that has the same name as the XAML file that contained the workflow definition. The function has the same parameters as the workflow and all common workflow parameters that PowerShell provides. This synthesized function references the workflow that was compiled in step #1 and stored in the process-wide cache. <\/font><\/p>\n<p align=\"justify\"><i><font size=\"3\">When the user executes the workflow, this PowerShell function is actually executed.<\/font><\/i><\/p>\n<p align=\"justify\"><font size=\"3\">4. When multiple clients connect to the workflow session configuration, all of these connections will be directed to the same process (<i>all of the information about session configurations warrants a blog post in itself<\/i>). In this case each of the sessions will contain a synthesized function but all of these functions will refer to the same entry in the cache.<\/font><\/p>\n<p><a href=\"http:\/\/devblogs.microsoft.com\/powershell\/wp-content\/uploads\/sites\/30\/2012\/06\/8055.image_thumb_6F3A0C86.png\"><img decoding=\"async\" src=\"http:\/\/devblogs.microsoft.com\/powershell\/wp-content\/uploads\/sites\/30\/2012\/06\/8055.image_thumb_6F3A0C86.png\" alt=\"command detail\" width=\"705\" height=\"115\" class=\"aligncenter size-full wp-image-14480\" srcset=\"https:\/\/devblogs.microsoft.com\/powershell\/wp-content\/uploads\/sites\/30\/2012\/06\/8055.image_thumb_6F3A0C86.png 705w, https:\/\/devblogs.microsoft.com\/powershell\/wp-content\/uploads\/sites\/30\/2012\/06\/8055.image_thumb_6F3A0C86-300x49.png 300w\" sizes=\"(max-width: 705px) 100vw, 705px\" \/><\/a><\/p>\n<p> <font size=\"3\"><\/font>  <\/p>\n<p align=\"justify\"><font size=\"3\">After a XAML file is imported, you can use <font size=\"4\" face=\"Courier New\">Get-Command<\/font> to look at the corresponding command. Note that its command type is &quot;workflow,&quot; even though the underlying implementation is a function. <\/font><\/p>\n<p> <font size=\"3\"><\/font>  <\/p>\n<h3><font size=\"3\">1.1.1 PowerShell Script to Workflow Conversion<\/font><\/h3>\n<p> <font size=\"3\"><\/font>  <\/p>\n<p align=\"justify\"><font size=\"3\">When you author a workflow in PowerShell (using the <font size=\"4\" face=\"Courier New\">workflow<\/font> keyword, as detailed in Part 1), the PowerShell parser converts it into an intermediate representation referred to in developer terms as an Abstract Syntax Tree (AST). The AST is then converted to a XAML document in memory. This XAML document goes through the same compilation and caching steps detailed above. It is possible to examine this XAML definition by using the <font size=\"4\" face=\"Courier New\">Get-Command<\/font> cmdlet. For example, consider the following workflow:<\/font><\/p>\n<p><a href=\"http:\/\/devblogs.microsoft.com\/powershell\/wp-content\/uploads\/sites\/30\/2012\/06\/1682.image_thumb_2F702601.png\"><img decoding=\"async\" src=\"http:\/\/devblogs.microsoft.com\/powershell\/wp-content\/uploads\/sites\/30\/2012\/06\/1682.image_thumb_2F702601.png\" alt=\"workflow 1\" width=\"378\" height=\"113\" class=\"aligncenter size-full wp-image-14481\" srcset=\"https:\/\/devblogs.microsoft.com\/powershell\/wp-content\/uploads\/sites\/30\/2012\/06\/1682.image_thumb_2F702601.png 378w, https:\/\/devblogs.microsoft.com\/powershell\/wp-content\/uploads\/sites\/30\/2012\/06\/1682.image_thumb_2F702601-300x90.png 300w\" sizes=\"(max-width: 378px) 100vw, 378px\" \/><\/a><\/p>\n<p> <font size=\"3\"><\/font>  <\/p>\n<p><font size=\"3\"><\/font><\/p>\n<p><a href=\"http:\/\/devblogs.microsoft.com\/powershell\/wp-content\/uploads\/sites\/30\/2012\/06\/0118.image_thumb_6FA63F7B.png\"><img decoding=\"async\" src=\"http:\/\/devblogs.microsoft.com\/powershell\/wp-content\/uploads\/sites\/30\/2012\/06\/0118.image_thumb_6FA63F7B.png\" alt=\"workflow 2\" width=\"715\" height=\"105\" class=\"aligncenter size-full wp-image-14482\" srcset=\"https:\/\/devblogs.microsoft.com\/powershell\/wp-content\/uploads\/sites\/30\/2012\/06\/0118.image_thumb_6FA63F7B.png 715w, https:\/\/devblogs.microsoft.com\/powershell\/wp-content\/uploads\/sites\/30\/2012\/06\/0118.image_thumb_6FA63F7B-300x44.png 300w\" sizes=\"(max-width: 715px) 100vw, 715px\" \/><\/a><\/p>\n<p><font size=\"3\">The converted XAML is available in the XamlDefinition property of each workflow.<\/font><\/p>\n<p> <font size=\"3\"><\/font>  <\/p>\n<p><a href=\"http:\/\/devblogs.microsoft.com\/powershell\/wp-content\/uploads\/sites\/30\/2012\/06\/1205.image_thumb_42251FB8.png\"><img decoding=\"async\" src=\"http:\/\/devblogs.microsoft.com\/powershell\/wp-content\/uploads\/sites\/30\/2012\/06\/1205.image_thumb_42251FB8.png\" alt=\"workflow 3\" width=\"715\" height=\"342\" class=\"aligncenter size-full wp-image-14483\" srcset=\"https:\/\/devblogs.microsoft.com\/powershell\/wp-content\/uploads\/sites\/30\/2012\/06\/1205.image_thumb_42251FB8.png 715w, https:\/\/devblogs.microsoft.com\/powershell\/wp-content\/uploads\/sites\/30\/2012\/06\/1205.image_thumb_42251FB8-300x143.png 300w\" sizes=\"(max-width: 715px) 100vw, 715px\" \/><\/a><\/p>\n<p> <font size=\"3\"><\/font>  <\/p>\n<p align=\"justify\"><font size=\"3\">Note that the <font size=\"4\" face=\"Courier New\">Get-WMIObject<\/font> cmdlet is converted into the <font size=\"4\" face=\"Courier New\">GetWmiObject<\/font> activity. There is also some additional transformation \u2013 like <font size=\"4\" face=\"Courier New\">&#8216;3:5:Get-BiosUsingScript&#8217;<\/font> \u2013 which is used to preserve the position information in the script. This is used when writing error messages that actually reference the position of a statement in the PowerShell script, rather than the converted XAML document.<\/font><\/p>\n<p> <font size=\"3\"><\/font>  <\/p>\n<p align=\"justify\"><font size=\"3\">In XAML and PowerShell workflows, the synthesized function is available in the <font size=\"4\" face=\"Courier New\">ScriptBlock<\/font> property of the workflow.<\/font><\/p>\n<p><a href=\"http:\/\/devblogs.microsoft.com\/powershell\/wp-content\/uploads\/sites\/30\/2012\/06\/8032.image_thumb_097A75AB.png\"><img decoding=\"async\" src=\"http:\/\/devblogs.microsoft.com\/powershell\/wp-content\/uploads\/sites\/30\/2012\/06\/8032.image_thumb_097A75AB.png\" alt=\"commandtype workflow\" width=\"720\" height=\"40\" class=\"aligncenter size-full wp-image-14484\" srcset=\"https:\/\/devblogs.microsoft.com\/powershell\/wp-content\/uploads\/sites\/30\/2012\/06\/8032.image_thumb_097A75AB.png 720w, https:\/\/devblogs.microsoft.com\/powershell\/wp-content\/uploads\/sites\/30\/2012\/06\/8032.image_thumb_097A75AB-300x17.png 300w\" sizes=\"(max-width: 720px) 100vw, 720px\" \/><\/a><\/p>\n<h2>1.2 Validation<\/h2>\n<p> <font size=\"3\"><\/font>  <\/p>\n<p align=\"justify\"><font size=\"3\">Validation is the process of verifying that all activities in the workflow are allowed. For performance reasons, the workflow is validated when it is executed, rather than at compile time. PowerShell allows you to define workflow sandboxes where you specify which activities can be part of a workflow in a given session configuration. If the workflow includes activities that are not allowed, validation fails and workflow execution will not progress.<\/font><\/p>\n<p><a href=\"http:\/\/devblogs.microsoft.com\/powershell\/wp-content\/uploads\/sites\/30\/2012\/06\/7077.image_thumb_30B4BEE0.png\"><img decoding=\"async\" src=\"http:\/\/devblogs.microsoft.com\/powershell\/wp-content\/uploads\/sites\/30\/2012\/06\/7077.image_thumb_30B4BEE0.png\" alt=\"allowedActivity\" width=\"724\" height=\"82\" class=\"aligncenter size-full wp-image-14485\" srcset=\"https:\/\/devblogs.microsoft.com\/powershell\/wp-content\/uploads\/sites\/30\/2012\/06\/7077.image_thumb_30B4BEE0.png 724w, https:\/\/devblogs.microsoft.com\/powershell\/wp-content\/uploads\/sites\/30\/2012\/06\/7077.image_thumb_30B4BEE0-300x34.png 300w\" sizes=\"(max-width: 724px) 100vw, 724px\" \/><\/a><\/p>\n<p> <font size=\"3\"><\/font>  <\/p>\n<p align=\"justify\"><font size=\"3\"><font size=\"4\" face=\"Courier New\">PSDefaultActivities<\/font> indicate that all PowerShell activities are allowed in the session configuration. Here is an example of validation failure.<\/font><\/p>\n<p><a href=\"http:\/\/devblogs.microsoft.com\/powershell\/wp-content\/uploads\/sites\/30\/2012\/06\/5808.image_thumb_6318925F.png\"><img decoding=\"async\" src=\"http:\/\/devblogs.microsoft.com\/powershell\/wp-content\/uploads\/sites\/30\/2012\/06\/5808.image_thumb_6318925F.png\" alt=\"get bios\" width=\"735\" height=\"232\" class=\"aligncenter size-full wp-image-14486\" srcset=\"https:\/\/devblogs.microsoft.com\/powershell\/wp-content\/uploads\/sites\/30\/2012\/06\/5808.image_thumb_6318925F.png 735w, https:\/\/devblogs.microsoft.com\/powershell\/wp-content\/uploads\/sites\/30\/2012\/06\/5808.image_thumb_6318925F-300x95.png 300w\" sizes=\"(max-width: 735px) 100vw, 735px\" \/><\/a><\/p>\n<p align=\"justify\"><font size=\"3\">Since the \u201c<font size=\"4\" face=\"Courier New\">TestWorkflow<\/font>\u201d custom session configuration was defined as a sandbox that allows only the <font size=\"4\" face=\"Courier New\">Get-Process<\/font> activity, the Get-Bios workflow fails because it uses the <font size=\"4\" face=\"Courier New\">Get-WmiObject<\/font> activity.<\/font><\/p>\n<p> <font size=\"3\"><\/font>  <\/p>\n<h2>2. Workflow Jobs Infrastructure<\/h2>\n<p> <font size=\"3\"><\/font>  <\/p>\n<p align=\"justify\"><font size=\"3\">As specified in <\/font><a href=\"http:\/\/blogs.msdn.com\/b\/powershell\/archive\/2012\/06\/15\/high-level-architecture-of-windows-powershell-workflow-part-1.aspx\"><font size=\"3\">Part 1 of this blog post<\/font><\/a><font size=\"3\">, in PowerShell, a workflow is always executed as a PowerShell job. The job infrastructure is not unique to PowerShell workflows but is an infrastructure provided by PowerShell that different modules can plug into. The PowerShell Workflow job infrastructure consists of the following four main components:<\/font><\/p>\n<p> <font size=\"3\"><\/font>  <\/p>\n<p><font size=\"3\">1. The PowerShell workflow job implementation (the <font size=\"4\" face=\"Courier New\">PSWorkflowJob<\/font> object).<\/font><\/p>\n<p> <font size=\"3\"><\/font>  <\/p>\n<p><font size=\"3\">2. A job manager that maintains the list of all workflow jobs and queries\/filters them.<\/font><\/p>\n<p> <font size=\"3\"><\/font>  <\/p>\n<p align=\"justify\"><font size=\"3\">3. A workflow <font size=\"4\" face=\"Courier New\">JobSourceAdapter<\/font>, which is the interface between the PowerShell job cmdlets and the workflow job manager.<\/font><\/p>\n<p> <font size=\"3\"><\/font>  <\/p>\n<p align=\"justify\"><font size=\"3\">4. Workflow Job Throttle Manager \u2013ensures that no more than a specified maximum number of jobs are running at any point in time<\/font><\/p>\n<p> <font size=\"3\"><\/font>  <\/p>\n<p align=\"justify\"><font size=\"3\">Since workflow jobs are built on the common job infrastructure, the PowerShell job cmdlets are used to manage the workflow jobs and they interact with the workflow job source adapter.<\/font><\/p>\n<p> <font size=\"3\"><\/font>  <\/p>\n<h2>2.1 Creation of Workflow Jobs<\/h2>\n<p> <font size=\"3\"><\/font>  <\/p>\n<p align=\"justify\"><font size=\"3\">When the user runs the workflow from the command line, the <font size=\"4\" face=\"Courier New\">ScriptBlock<\/font> associated with the synthesized function is executed. Here is what happens:<\/font><\/p>\n<p> <font size=\"3\"><\/font>  <\/p>\n<p align=\"justify\"><font size=\"3\">1. The Activity object for the specified workflow is obtained from the process-wide cache and it is validated as specified in the validation section (1.2).<\/font><\/p>\n<p> <font size=\"3\"><\/font>  <\/p>\n<p align=\"justify\"><font size=\"3\">2. If validation succeeds, a workflow job is created for the specified workflow. When the user specifies multiple computers (by using the <font size=\"4\" face=\"Courier New\">PSComputerName<\/font> parameter), a child workflow job is created under the parent job for each computer that is targeted. This way, all workflow job instances created by user in a particular invocation are managed together. (<i>Here is where the caching comes in handy. Since the same definition is used, a lot of time is saved by compiling only once. Caching of compiled workflows is one of the first performance improvements we made<\/i>). <\/font><\/p>\n<p> <font size=\"3\"><\/font>  <\/p>\n<p align=\"justify\"><font size=\"3\">3. The workflow job is submitted to the job throttle manager which invokes the job when it is permitted to do so under the current throttling policies.<\/font><\/p>\n<p> <font size=\"3\"><\/font>  <\/p>\n<p align=\"justify\"><font size=\"3\">4. If the user invoked the workflow by using the <font size=\"4\" face=\"Courier New\">AsJob<\/font> parameter, the parent job object is written to the pipeline. If the user did not specify the <font size=\"4\" face=\"Courier New\">AsJob<\/font> parameter, the synthesized function mimics synchronous behavior for the workflow.<\/font><\/p>\n<p> <font size=\"3\"><\/font>  <\/p>\n<h2>3. Activities<\/h2>\n<p> <font size=\"3\"><\/font>  <\/p>\n<p align=\"justify\"><font size=\"3\">Each workflow job instantiates a <\/font><a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/system.activities.workflowapplication.aspx\"><font size=\"4\" face=\"Courier New\">WorkflowApplication<\/font><\/a><font size=\"3\"> using its compiled workflow definition it retrieved from the process-wide cache. As mentioned in <\/font><a href=\"http:\/\/blogs.msdn.com\/b\/powershell\/archive\/2012\/06\/15\/high-level-architecture-of-windows-powershell-workflow-part-1.aspx\"><font size=\"3\">Part 1<\/font><\/a><font size=\"3\">, <font size=\"4\" face=\"Courier New\">WorkflowApplication<\/font> provides a host for a single workflow instance. Since a workflow is a series of activities, the workflow engine is responsible for coordinating the execution of these activities. <\/font><\/p>\n<p> <font size=\"3\"><\/font>  <\/p>\n<p align=\"justify\"><font size=\"3\">PowerShell workflows can contain different kinds of activities. These can be broadly divided into three categories:<\/font><\/p>\n<p> <font size=\"3\"><\/font>  <\/p>\n<p align=\"justify\"><font size=\"3\">1. Activities supplied by Windows Workflow Foundation such as the flow control activities (while loops, if statements, etc)<\/font><\/p>\n<p> <font size=\"3\"><\/font>  <\/p>\n<p align=\"justify\"><font size=\"3\">2. Activities that represent PowerShell commands. This includes all the built-in activities supplied by PowerShell.<\/font><\/p>\n<p> <font size=\"3\"><\/font>  <\/p>\n<p align=\"justify\"><font size=\"3\">3. Special activities \u2013 like <font size=\"4\" face=\"Courier New\">Pipeline, PowerShellValue, GetPSWorkflowData<\/font> and <font size=\"4\" face=\"Courier New\">SetPSWorkflowData<\/font>. These are primarily \u201chelper\u201d activities and they are mostly used when a workflow authored in the PowerShell language is translated into a XAML. <i>We will provide details about these activities in an upcoming blog post about workflow authoring.<\/i><\/font><\/p>\n<p> <font size=\"3\"><\/font>  <\/p>\n<p align=\"justify\"><font size=\"3\"><font size=\"4\" face=\"Courier New\">PSActivity<\/font> is the base for all PowerShell activities. Deriving from this base class, there are a number of subclasses that encapsulate differences in remoting behavior exposed by some commands. For example, some commands like the WMI and CIM cmdlets are capable of handling remoting on their own. The activities wrapping these cmdlets derive from a special subclass that coordinates their execution. Other activities such as <font size=\"4\" face=\"Courier New\">Write-Output<\/font> are never executed remotely. The majority of activities, however, should be executed remotely but are not themselves capable of remote execution. These activities derive from <font size=\"4\" face=\"Courier New\">PSRemotingActivity<\/font>. Any activity derived from <font size=\"4\" face=\"Courier New\">PSRemotingActivity<\/font> gets default remoting support using PowerShell remoting. Since Workflows are meant for multi-machine management, it is important that an activity is able to perform a task on a remote computer. <\/font><\/p>\n<p> <font size=\"3\"><\/font>  <\/p>\n<p align=\"justify\"><font size=\"3\">We wanted to make it really easy for developers to develop activities. Developers just concentrate on developing cmdlets for use in PowerShell. A set of APIs can then be used to generate activities from these cmdlets. These activities now have remoting capabilities and can be used in workflows to target different computers. <\/font><\/p>\n<p> <font size=\"3\"><\/font>  <\/p>\n<h2>3.1 Determining Command Execution Type<\/h2>\n<p> <font size=\"3\"><\/font>  <\/p>\n<p><font size=\"3\">As mentioned in Part 1, the command associated with an activity can be run in one of the following ways:<\/font><\/p>\n<p> <font size=\"3\"><\/font>  <\/p>\n<p><font size=\"3\">1. In process \u2013 command runs within the hosting process<\/font><\/p>\n<p> <font size=\"3\"><\/font>  <\/p>\n<p><font size=\"3\">2. Out-of-process &#8211; command is run in a separate PowerShell process<\/font><\/p>\n<p> <font size=\"3\"><\/font>  <\/p>\n<p><font size=\"3\">3. Remotely &#8211; command is run on a remote machine<\/font><\/p>\n<p> <font size=\"3\"><\/font>  <\/p>\n<p><font size=\"3\">How the command is run is determined as follows:<\/font><\/p>\n<p> <font size=\"3\"><\/font>  <\/p>\n<p align=\"justify\"><font size=\"3\">1. All CIM and WMI activities always run in-process (<i>these are first-class citizens and have special handling within PowerShell)<\/i> to provide maximal performance and scalability.<\/font><\/p>\n<p> <font size=\"3\"><\/font>  <\/p>\n<p align=\"justify\"><font size=\"3\">2. If the session configuration allows the activity to be run in-process, the command is executed within the host process<\/font><\/p>\n<p> <font size=\"3\"><\/font>  <\/p>\n<p align=\"justify\"><font size=\"3\">3. If the session configuration specifies that the activity needs to run out of process, the Activity Controller runs it in a separate process <\/font><\/p>\n<p> <font size=\"3\"><\/font>  <\/p>\n<p align=\"justify\"><font size=\"3\">4. If <font size=\"4\" face=\"Courier New\">PSComputerName<\/font> parameter is used, the command is executed remotely against the specified computer.<\/font><\/p>\n<p> <font size=\"3\"><\/font>  <\/p>\n<h2>3.2 Activity Execution<\/h2>\n<p> <font size=\"3\"><\/font>  <\/p>\n<p align=\"justify\"><font size=\"3\">1. WMI and CIM activities are special in that they always run in-process and PowerShell directly talks to the underlying infrastructure to execute these commands \u2013 either locally or remotely.<\/font><\/p>\n<p> <font size=\"3\"><\/font>  <\/p>\n<p align=\"justify\"><font size=\"3\">2. For every other activity, there is one commonality \u2013 each command (pertaining to an activity) in a workflow is executed in its own runspace. This is true for all the types specified in #2 to #4 above. <\/font><\/p>\n<p> <font size=\"3\"><\/font>  <\/p>\n<p align=\"justify\"><font size=\"3\">3. When a command runs in-process, a runspace is obtained from a local cache of runspaces, the command is executed; the runspace is cleaned up and returned to the cache (<i>Maintaining a cache of runspaces is a performance-enhancing feature of Windows PowerShell Workflows<\/i>).<\/font><\/p>\n<p> <font size=\"3\"><\/font>  <\/p>\n<p align=\"justify\"><font size=\"3\">4. When a command is run on a remote machine, a runspace is obtained from the Connection Manager, the command is executed, and the runspace is returned to the Connection Manager.<\/font><\/p>\n<p> <font size=\"3\"><\/font>  <\/p>\n<p align=\"justify\"><font size=\"3\">5. When a command needs to be run out-of-process, the command is submitted to the Activity Controller. The Activity Controller maintains a pool of PowerShell worker processes in which it runs the command and calls back when it is done.<\/font><\/p>\n<p> <font size=\"3\"><\/font>  <\/p>\n<p align=\"justify\"><font size=\"3\">If an activity takes a long time to run, the workflow may be passivated i.e. unloaded from memory, and all the information associated with the workflow is saved. After the activity completes, it reactivates the workflow i.e. loads the workflow\u2019s state back into memory, and resumes execution.<\/font><\/p>\n<p> <font size=\"3\"><\/font>  <\/p>\n<h2>4. Connection Manager<\/h2>\n<p> <font size=\"3\"><\/font>  <\/p>\n<p align=\"justify\"><font size=\"3\">As mentioned in part 1, the Connection Manager is responsible for pooling, throttling and managing all connections from workflows within the process. The Connection Manager maintains a pool of connections indexed first by the computer name and then by the session configuration name. <\/font><\/p>\n<p> <font size=\"3\"><\/font>  <\/p>\n<h2>4.1 Servicing a Connection Request<\/h2>\n<p> <font size=\"3\"><\/font>  <\/p>\n<p align=\"justify\"><font size=\"3\">1. When an activity requests a connection to a specified computer, the Connection Manager checks to see if there is an available connection that matches all the required remoting parameters (computer name, authentication, credentials, etc). If it finds one, it marks the connection as busy, and returns the existing connection. <\/font><\/p>\n<p> <font size=\"3\"><\/font>  <\/p>\n<p align=\"justify\"><font size=\"3\">2. If there is no match, it checks to see if the maximum allowed connections to a particular computer have been reached. If there is room for creation, it creates a new connection with the specified parameters. <\/font><\/p>\n<p> <font size=\"3\"><\/font>  <\/p>\n<p align=\"justify\"><font size=\"3\">3. If there are open connections, but they do not match the required remoting parameters, and the maximum allowed connections to a computer has been reached, the Connection Manager finds an open connection that is free, closes that connection, and then creates a new one with the required parameters. This ensures that no more than the maximum number of remote connections is created to a particular computer.<\/font><\/p>\n<p> <font size=\"3\"><\/font>  <\/p>\n<p align=\"justify\"><font size=\"3\">4. When there are no available existing connections, and a new connection cannot be created, the request is queued until a connection is freed up.<\/font><\/p>\n<p> <font size=\"3\"><\/font>  <\/p>\n<p align=\"justify\"><i><font size=\"3\"> To maintain a fair servicing policy, Connection Manager ensures that queued requests are serviced before new incoming requests.<\/font><\/i><\/p>\n<p> <font size=\"3\"><\/font>  <\/p>\n<h2>4.2 Closing Connections Not in Use<\/h2>\n<p> <font size=\"3\"><\/font>  <\/p>\n<p align=\"justify\"><font size=\"3\">A timer in the Connection Manager periodically iterates through all the connections available and checks if a connection is busy or free. If the connection is free, it is flagged. If the connection is already flagged (which means it is free since the last check), it is closed. When a connection is open, it not only amounts to resource consumption on the local computer, but there is also an active, but unused process on the remote computer. Closing a connection ensures that a process does not stay open on the remote computer unless it is necessary.<\/font><\/p>\n<p> <font size=\"3\"><\/font>  <\/p>\n<h2>4.3 Disconnect\/Reconnect Operations<\/h2>\n<p> <font size=\"3\"><\/font>  <\/p>\n<p align=\"justify\"><font size=\"3\">When the number of outbound connections increases, the Connection Manager starts to disconnect the connections. It then connects to the remote computers few at a time to obtain the data. This ensures that PowerShell Workflows can connect to a large set of machines and start commands and then obtain data in a controlled manner. There is also a limit to the total number of disconnected sessions that the Connection Manager will maintain.<\/font><\/p>\n<p><a href=\"http:\/\/devblogs.microsoft.com\/powershell\/wp-content\/uploads\/sites\/30\/2012\/06\/4426.image_thumb_0A52DB95.png\"><img decoding=\"async\" src=\"http:\/\/devblogs.microsoft.com\/powershell\/wp-content\/uploads\/sites\/30\/2012\/06\/4426.image_thumb_0A52DB95.png\" alt=\"Get PSSession Configuration\" width=\"718\" height=\"99\" class=\"aligncenter size-full wp-image-14487\" srcset=\"https:\/\/devblogs.microsoft.com\/powershell\/wp-content\/uploads\/sites\/30\/2012\/06\/4426.image_thumb_0A52DB95.png 718w, https:\/\/devblogs.microsoft.com\/powershell\/wp-content\/uploads\/sites\/30\/2012\/06\/4426.image_thumb_0A52DB95-300x41.png 300w\" sizes=\"(max-width: 718px) 100vw, 718px\" \/><\/a><\/p>\n<p align=\"justify\"><i><font size=\"3\">MaxSessionPerRemoteNode is the maximum number of connections that will be made to each remote computer.<\/font><\/i><\/p>\n<p> <font size=\"3\"><\/font>  <\/p>\n<h2>4.4 Throttling<\/h2>\n<p> <font size=\"3\"><\/font>  <\/p>\n<p align=\"justify\"><font size=\"3\">The Connection Manager throttles all operations \u2013 <font size=\"4\" face=\"Courier New\">open, close, connect<\/font> and <font size=\"4\" face=\"Courier New\">disconnect<\/font> &#8211; using a single queue. This ensures that there are no more than a specified maximum number of network operations in progress. The throttling works similar to the throttling in PowerShell remoting.<\/font><\/p>\n<p> <font size=\"3\"><\/font>  <\/p>\n<h2>5. Activity Controller<\/h2>\n<p> <font size=\"3\"><\/font>  <\/p>\n<p align=\"justify\"><font size=\"3\">Whenever an activity is considered unreliable (<i>like a script that was grabbed from a blog post<\/i>), the workflow author or the administrator might want to run the activity in a separate process. This will ensure that, in case there is a crash, the whole session is not brought down; only the activity fails. The Activity Controller maintains a pool of PowerShell processes and is responsible for executing activities out of process. <\/font><\/p>\n<p> <font size=\"3\"><\/font>  <\/p>\n<h2>5.1 Executing Activities Out of Process<\/h2>\n<p> <font size=\"3\"><\/font>  <\/p>\n<p align=\"justify\"><font size=\"3\">The Activity Controller maintains an unbounded queue of incoming requests. The request typically contains the command to execute and its input, the output buffer, the variables that need to set and the modules that need to be made available for the command to execute.<\/font><\/p>\n<p> <font size=\"3\"><\/font>  <\/p>\n<p align=\"justify\"><font size=\"3\">The activities that will be run out of process, the number of processes that the Activity Controller will create, and the amount of time a process will stay idle, are determined by the properties of the session configuration.<\/font><\/p>\n<p><a href=\"http:\/\/devblogs.microsoft.com\/powershell\/wp-content\/uploads\/sites\/30\/2012\/06\/1205.image_thumb_1C9BA257.png\"><img decoding=\"async\" src=\"http:\/\/devblogs.microsoft.com\/powershell\/wp-content\/uploads\/sites\/30\/2012\/06\/1205.image_thumb_1C9BA257.png\" alt=\"activity controller screenshot\" width=\"726\" height=\"79\" class=\"aligncenter size-full wp-image-14488\" srcset=\"https:\/\/devblogs.microsoft.com\/powershell\/wp-content\/uploads\/sites\/30\/2012\/06\/1205.image_thumb_1C9BA257.png 726w, https:\/\/devblogs.microsoft.com\/powershell\/wp-content\/uploads\/sites\/30\/2012\/06\/1205.image_thumb_1C9BA257-300x33.png 300w\" sizes=\"(max-width: 726px) 100vw, 726px\" \/><\/a><\/p>\n<p> <font size=\"3\"><\/font>  <\/p>\n<p align=\"justify\"><font size=\"3\">A servicing thread in the Activity Controller picks requests from the queue, creates a new PowerShell process if required, creates a new runspace (out-of-process runspace) for every command execution, prepares the runspace, and finally executes the command. Once complete the Activity Controller calls back into the activity and the workflow execution resumes.<\/font><\/p>\n<p> <font size=\"3\"><\/font>  <\/p>\n<p align=\"justify\"><font size=\"3\">If the process crashes during execution of the command, the activity fails and the workflow handles this failure.<\/font><\/p>\n<p> <font size=\"3\"><\/font>  <\/p>\n<h2>6. Persistence<\/h2>\n<p> <font size=\"3\"><\/font>  <\/p>\n<p align=\"justify\"><font size=\"3\">Persistence is the operation within the execution of the workflow that saves information about the workflow to a store. Windows PowerShell Workflow uses a file-based store. The persisted information consists of the workflow definition, workflow parameters, workflow job state, internal state information, input and output, metadata associated with the workflow, etc.<\/font><\/p>\n<p> <font size=\"3\"><\/font>  <\/p>\n<p><font size=\"3\">Every workflow has a \u2013<font size=\"4\" face=\"Courier New\">PSPersist<\/font> common parameter that has a tri-state value:<\/font><\/p>\n<p> <font size=\"3\"><\/font>  <\/p>\n<p><font size=\"3\">1. Not specified \u2013 persist at the beginning and end of a workflow<\/font><\/p>\n<p> <font size=\"3\"><\/font>  <\/p>\n<p><font size=\"3\">2. <font size=\"4\" face=\"Courier New\">$true<\/font> \u2013 persist after execution of every activity<\/font><\/p>\n<p> <font size=\"3\"><\/font>  <\/p>\n<p><font size=\"3\">3. <font size=\"4\" face=\"Courier New\">$false<\/font> \u2013 do not persist at all<\/font><\/p>\n<p> <font size=\"3\"><\/font>  <\/p>\n<p align=\"justify\"><font size=\"3\">Persistence can also be managed within the workflow itself. If the workflow has a <font size=\"4\" face=\"Courier New\">CheckPoint-Workflow<\/font> or a <font size=\"4\" face=\"Courier New\">Suspend<\/font> activity specified at any point, persistence happens at those points as well.<\/font><\/p>\n<p> <font size=\"3\"><\/font>  <\/p>\n<p><font size=\"3\">When a workflow is resumed, it always resumes from the last persisted point.<\/font><\/p>\n<p> <font size=\"3\"><\/font>  <\/p>\n<h2>6.1 Begin Persistence<\/h2>\n<p> <font size=\"3\"><\/font>  <\/p>\n<p align=\"justify\"><font size=\"3\">All the information, including the workflow definition and the input, are persisted when the workflow job is created. This is because a workflow job may not start running as soon as it is created due to throttling. Before the start of the workflow, if the process restarts (for whatever reason), the job can simply be resumed since all the parameters and input are available. This helps when there are a large number of jobs being executed, which is typical when managing a large number of machines.<\/font><\/p>\n<p> <font size=\"3\"><\/font>  <\/p>\n<h2>6.2 End Persistence<\/h2>\n<p> <font size=\"3\"><\/font>  <\/p>\n<p align=\"justify\"><font size=\"3\"><i>End persistence<\/i> is persistence that happens when the workflow reaches a terminal state (<font size=\"4\" face=\"Courier New\">Completed<\/font>, <font size=\"4\" face=\"Courier New\">Stopped<\/font> or <font size=\"4\" face=\"Courier New\">Failed<\/font>). The idea is that a user will be able to retrieve output and state of the workflow after a process restart. <\/font><\/p>\n<p> <font size=\"3\"><\/font>  <\/p>\n<p align=\"justify\"><font size=\"3\">Even if a user specifies \u2013<font size=\"4\" face=\"Courier New\">PSPersist<\/font> as $<font size=\"4\" face=\"Courier New\">false<\/font>, we attempt to see if output from the workflow and other information can be persisted. If so, it will be persisted because it frees up memory resources. This helps Windows PowerShell Workflow scale to a large number of jobs. (<i>This is one of the scalability improvements. As a result, the number of jobs that can be executed simultaneously is very large).<\/i><\/font><\/p>\n<p> <font size=\"3\"><\/font>  <\/p>\n<h2>6.3 Persistence at Persistence Point<\/h2>\n<p> <font size=\"3\"><\/font>  <\/p>\n<p align=\"justify\"><font size=\"3\">When there is a <font size=\"4\" face=\"Courier New\">CheckPoint-Workflow<\/font> or a <font size=\"4\" face=\"Courier New\">Suspend<\/font> activity in a workflow, persistence takes place at that point. This allows the workflow author to specify logical points in the execution sequence where it makes sense for the workflow to be resumed from that point.<\/font><\/p>\n<p> <font size=\"3\"><\/font>  <\/p>\n<h2>7. Conclusion<\/h2>\n<p> <font size=\"3\"><\/font>  <\/p>\n<p align=\"justify\"><font size=\"3\">This blog post concludes the two-part series on the high level architecture of Windows PowerShell Workflow.<\/font><\/p>\n<p align=\"justify\">&#160;<\/p>\n<p align=\"left\"><font size=\"3\">Narayanan Lakshmanan [MSFT]&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; <\/font><font size=\"3\">Software Design Engineer \u2013 Windows PowerShell&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; <\/font><font size=\"3\">Microsoft Corporation<\/font><\/p>\n","protected":false},"excerpt":{"rendered":"<p>This is the second part of our post on the high level architecture of Windows PowerShell Workflow. Part 1 of this blog post provided an overview of the architecture and its various components. This post will go into more detail on the various subcomponents and provide some insight into the internals of Windows PowerShell Workflow. [&hellip;]<\/p>\n","protected":false},"author":600,"featured_media":13641,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[272,352],"class_list":["post-2321","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-powershell","tag-powershell-workflow","tag-windows-server-2012"],"acf":[],"blog_post_summary":"<p>This is the second part of our post on the high level architecture of Windows PowerShell Workflow. Part 1 of this blog post provided an overview of the architecture and its various components. This post will go into more detail on the various subcomponents and provide some insight into the internals of Windows PowerShell Workflow. [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/powershell\/wp-json\/wp\/v2\/posts\/2321","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/powershell\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/powershell\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/powershell\/wp-json\/wp\/v2\/users\/600"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/powershell\/wp-json\/wp\/v2\/comments?post=2321"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/powershell\/wp-json\/wp\/v2\/posts\/2321\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/powershell\/wp-json\/wp\/v2\/media\/13641"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/powershell\/wp-json\/wp\/v2\/media?parent=2321"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/powershell\/wp-json\/wp\/v2\/categories?post=2321"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/powershell\/wp-json\/wp\/v2\/tags?post=2321"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}