Summary: Boe Prox presents some tips about beginning use with runspaces.
Honorary Scripting Guy and Cloud and Datacenter Management MVP, Boe Prox, here today filling in for my good friend, The Scripting Guy. Today I begin a 3-part series about working with PowerShell runspaces. I'll finish up with a fourth post that talks about a module I put together that makes working with runspaces as simple as using some similar commands that you are already familiar with.
Yesterday in Beginning Use of PowerShell Runspaces: Part 1, we spent some time looking at how we can create a runspace that can be used to run a command in the background while leaving our console wide open for us to continue running commands. Although using what we have learned will make it easier to push commands to the background, we are currently limited to only running what we have in the script block.
This means that we have to throw everything into the script block to run. This doesn’t allow us to dynamically create variables or to have parameters to use to run against a collection of items. Fortunately, we have this capability when working with runspaces, but we have to make sure that everything is done correctly; otherwise, we may have some unforeseen issues.
Why can’t we simply create variables outside of the script block, run the commands in the runspace, and wait for our results to come rolling in? Well, the following example shows how this might not fare so well.
$PowerShell = [powershell]::Create()
$Global:Param1 = 'Param1'
$Global:Param2 = 'Param2'
[void]$PowerShell.AddScript({
[pscustomobject]@{
Param1 = $Param1
Param2 = $Param2
}
})
#Invoke the command
$PowerShell.Invoke()
$PowerShell.Dispose()
Yes, the Global is intentional to show you that regardless of the type of scope that you give the variable, it will not work within the runspace. It simply outputs an object that consists of nothing useful.
To get the desired results, we need to make use of a method called AddArgument(), and give it our data. There are some things to keep in mind when using this approach. First, you must have a Param() statement within your script block so it can populate the variables. Even more importantly, you must be aware of the order that you supply the arguments with each AddArgument() method. If you switch the order of what is being added, you will have issues, for example:
$Param1 = 'Param1'
$Param2 = 'Param2'
$PowerShell = [powershell]::Create()
[void]$PowerShell.AddScript({
Param ($Param1, $Param2)
[pscustomobject]@{
Param1 = $Param1
Param2 = $Param2
}
}).AddArgument($Param2).AddArgument($Param1)
#Invoke the command
$PowerShell.Invoke()
$PowerShell.Dispose()
This doesn’t work out that well. Order is absolutely important in this approach. This code will work out much better:
$Param1 = 'Param1'
$Param2 = 'Param2'
$PowerShell = [powershell]::Create()
[void]$PowerShell.AddScript({
Param ($Param1, $Param2)
[pscustomobject]@{
Param1 = $Param1
Param2 = $Param2
}
}).AddArgument($Param1).AddArgument($Param2)
#Invoke the command
$PowerShell.Invoke()
$PowerShell.Dispose()
This is nice, but we can make it a little easier by using the AddParameter() method instead. This gives us an easier approach to specifying the proper variables with the parameters without the need to worry about the order of adding the data. The only thing that you must be aware of is that the parameter name must match what is in the Param() statement in the script block so it maps correctly.
We have two options for adding parameters. The first is to individually add them, such as .AddParameter(‘Param1’,$Param1). This works, but if you start adding several parameters to a runspace, you may have to get creative with how you format your code.
$Param1 = 'Param1'
$Param2 = 'Param2'
$PowerShell = [powershell]::Create()
[void]$PowerShell.AddScript({
Param ($Param1, $Param2)
[pscustomobject]@{
Param1 = $Param1
Param2 = $Param2
}
}).AddParameter('Param2',$Param2).AddParameter('Param1',$Param1) #Order won't matter now
#Invoke the command
$PowerShell.Invoke()
$PowerShell.Dispose()
A more useful approach is to create a hash table with the parameter name and its value. Then supply that to the AddParameters() method, which will take each item in the hash table and map them to what you have in Param().
$ParamList = @{
Param1 = 'Param1'
Param2 = 'Param2'
}
$PowerShell = [powershell]::Create()
[void]$PowerShell.AddScript({
Param ($Param1, $Param2)
[pscustomobject]@{
Param1 = $Param1
Param2 = $Param2
}
}).AddParameters($ParamList)
#Invoke the command
$PowerShell.Invoke()
We have the same output as we got previously, but now have better looking code to go along with it!
Now we have covered how to handle parameters within a runspace. Tomorrow, we will continue our dive into runspaces by looking at runspace pools and the world of multithreading our commands!
I invite you to follow the Scripting Guy on Twitter and Facebook. If you have any questions, send email to the Scripting Guy at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. Until then, see ya!
Boe Prox, Windows PowerShell MVP and Honorary Scripting Guy