InlineScripts and Variables in PowerShell Workflows

Quick Tutorial:

1) Note that not all PowerShell operations translate into Workflow activities
2) Place all PowerShell-only cmdlets into an InlineScript block
3) Pull Workflow variables into the InlineScript block by prefixing the variable with $Using:
4) Save the output of the inline script by assigning it to a Workflow variable

Workflow InlineScriptExample
{
    $WorkflowVariable
    $CapturedScriptOutput = Inlinescript
    {
        $Using:WorkflowVariable
    }
}

 


Full Tutorial:

In order to understand variables as they apply to workflows in the context of PowerShell scripting, it is necessary to realize that workflows are not typical PowerShell scripts. Workflows (As apposed to functions) utilize the Windows Workflow Foundation engine for managing activities. You can express many Workflow activities using the same statements already used in PowerShell (Thanks Microsoft!). For example, the functionally equivalent PowerShell function:

function test {
    Get-WmiObject win32_Processor
}

would be expressed as the workflow:

Workflow test {
    Get-WmiObject win32_Processor
}

The syntax is the same, the output is the same, but the way we arrived is different. This is all important to understand because it will help us to comprehend how variables can be moved between workflows and typical PowerShell scripts so that we can take advantage of the functionality of both.

When do I need to utilize PowerShell capabilities in my Workflow?

Suppose I want to perform a ToUpper() operation on a string I am using in my workflow.ToUpper() is a PowerShell operation that does not translate into a Windows Workflow activity. If I try to perform the operation, PowerShell ISE will instruct me to include the statement in an InlineScript block.

Workflow test
{
    InlineScript
    {
        #Insert PowerShell only commands here
    }
}

When I use an InlineScript, I am literally launching a PowerShell instance to do the work that I can’t do within the workflow context. For this reason, all variables must be hand-delivered (figuratively speaking) to the InlineScript, and any data we want to save out must be returned to the Workflow.

Passing Workflow Variables to InlineScripts and Capturing Return Values

Now that we understand what an InlineScript block is really doing, working with variables within it becomes simple. If I have a variable that I need to use within the InlineScript, I need to use the “Using:” scope modifier. The variable is then passed in. If then I need to get a value out of the InlineScript, I return the variable I need. For Example:

Workflow Get-AirForceValues
{
    $Values = "Integrity-Service-Excellence"

    #Starts InlineScript activity invoking a new process
    #Saves return data to $Values
    $Values = InlineScript
    {
        #Passes the $Values variable to the InlineScript
        $NewValues = $Using:Values

        $NewValues = $NewValues.Toupper()
        $NewValues
    }
    $Values
}

Running this Workflow then returns the string “INTEGRITY-SERVICE-EXCELLENCE”. You can see that we were able to make a workflow variable visible to the InlineScript and then get back data when the InlineScript terminated.

Creating and Adding Your Own Modules to PowerShell

Quick Tutorial:

  1. Make sure your script is written as a function, named in the proper Verb-Noun format
  2. Save the file as <Verb>-<Noun>.psm1 in a folder named <Verb>-<Noun>
  3. Save the <Verb>-<Noun> folder in a custom modules folder
  4. If your PowerShell profile for your current session does not yet exist. Create it, then edit it:
    New-Item -Path $Profile -Type File -Force
    Notepad $Profile
    
  5. Once open in notepad, add the following line to the profile:
    $Env:PSModulePath = $Env:PSModulePath + ";C:\Path\to\Custom\Modules"
  6. Save the profile and restart your PowerShell session

 


Full Tutorial:

“How can I create my own modules that I can run from the PowerShell Console without using Import-Module every time?” is the question that prompted this. Here’s the answer (I’m using PowerShell 4.0):

Create and Store Your Modules

First, I recommend that you create a file for each PowerShell function that you create. Name the function with the conventional camel-case <verb>-<noun> format used in PowerShell (e.g. RemoveArchEnemy).

example1 Use Recommended verbs as described here:
Approved Verbs for Windows PowerShell Commands

Then, name your file the same as your function name and use psm1 as the file type. This indicates that the file is a module. For example, if your function was called Remove-ArchEnemy then your file would be

Remove-ArchEnemy.psm1

example21

Give your module its own folder, also named after the name of the function, then find a place to keep your modules forever. A good example would be:

C:\Users\<username>\Documents\WindowsPowerShell\Modules

Using this, a complete path to our example would then be:

C:\Users\<username>\Documents\WindowsPowerShell\Modules\Remove-ArchEnemy\Remove-ArchEnemy.psm1

example3

Setting Up PowerShell Profile and Adding Modules

PowerShell has a number of profiles. These correspond to different users and between the simple console and ISE. I won’t go into that here (For more on Profiles click here). For this exercise I will use the example of the PowerShell ISE environment which I use almost exclusively.

In the console type:

$Profile

My machine returns:

C:\Users\Jonathan\Documents\WindowsPowerShell\Microsoft.PowerShellISE_profile.ps1

Again, this will vary depending on the profile in use for your console. In reality, the profile is just a script that runs at the beginning of each PowerShell session.

You may notice that the path to the profile file listed doesn’t even exist. If this is the case than we need to create it. We then need to edit it. This can be done with two commands:

New-Item -Path $Profile -Type File -Force
Notepad $Profile

With the profile now ready for editing we need to add a value to the PSModulePath environment variable. This is where all of the valid module paths are located. Add the following to your profile:

$Env:PSModulePath = $Env:PSModulePath + ";C:\Users\Documents\WindowsPowerShell\Modules"

At the start of each PowerShell session the new path is now automatically added to the PSModulePath.

Save the file and restart PowerShell. Now, you should be able to call all of your custom functions from the PowerShell console. Also, this works just as well with Windows Workflow Foundation modules if you’re cool like that.