Out Parameters, Nested Classes, Specifying A Proxy, Modules And Local Variables And Debugging Jobs In PowerShell

In post I’m going to cover out parameters, nested classes, specifying a proxy, modules and local variables and debugging jobs in PowerShell

Calling .NET Functions With Out Parameters
When you call a .NET function that requires an out parameter you have to cast the parameter as a System.Management.Automation.PSReference object by using a [ref] cast. Apart from the cast you also have to initialize the variable, this didn’t seem obvious at first since you don’t have to do it in .NET but in PowerShell you have to.

$parsedip = New-Object System.Net.IPAddress -ArgumentList 1
[System.Net.IPAddress]::TryParse("192.168.10.88", [ref] $parsedip)

Using Nested .NET Classes In PowerShell
Usually you don’t come across nested classes very often but when you do they have different syntax from other classes. To access a nested class use the + sign before the name of the nested class instead of a full stop.

Microsoft.Exchange.WebServices.Data.SearchFilter+IsLessThanOrEqualTo

Specifying A Proxy At Script Level
If you want to specify a proxy that will be used for web requests but you can’t or don’t want to change the proxy settings machine wide you can specify it in your script.

$proxyString = "http://someserver:8080"
$proxyUri = new-object System.Uri($proxyString)
[System.Net.WebRequest]::DefaultWebProxy = new-object System.Net.WebProxy ($proxyUri, $true)

Modules And Local Variables
Variables declared local to a module will not hold their value between function calls to the module. For example if you create a counter that is incremented every time you call a function in the module that variable will be created as a new local  variable every time the function begins execution. To reference the same variable between function calls you have to declare it at script scope. This will not clash with variables declared in scripts that use the module if they also have a variable with the same name declared in script scope.

$script:localvar = 0

function DoOne
{
 write-output "DoOne : $script:localvar"
 $script:localvar += 1
}

Debugging Jobs
Since jobs start their own Powershell.exe process when you run them you can’t step into the code from your IDE even if you have a breakpoint set in the script. You have to use the Debug-Job cmdlet in your script running the job to start a debug session to the remote process. It will start debugging at the currently executing line in the job. If you want the job to wait for you at a specific line so you can start debugging at that point use the Wait-Debugger cmdlet in the script being executed by the job.

Francois Delport

PowerShell Background Jobs

If you want to run PowerShell commands in parallel you can use Powershell background jobs. In this post I’m going to have a look at waiting for jobs to finish, handling failures, timeouts and some other bits I came across when I started using background jobs.

Start-Job
To start a background job use Start-Job and pass in the script block you want to execute, PowerShell will immediately return to the console.

PowerShell Background Jobs

The job will execute in it’s own scope and won’t be able to use variables and functions declared in the script starting the job. You have to pass in all the functions required for the script block to run using the InitializationScript parameter when you call Start-Job. You can store the contents of the script block in a variable to enable reuse.

$functions =
 {
   ... 
   functions
   ...
}

If you have multiple jobs running you will need a way to know all of them  completed execution. Complete in this case means it ran through the script block you passed it, it could have failed, or thrown exceptions or got disconnected from the remote machine or executed without errors. You can use Wait-Job and or Receive-Job for this.

Wait-Job
Wait-Job suppresses the console output until background jobs are completed and then it prints only the job state. Note the HasMoreData property that is true, this means there is something in the job output that can be retrieved. By default it will wait for all the jobs in the current session but you can pass in filters to wait for specific jobs. Only problem with Wait-Job is it doesn’t show the output from jobs, you can always log to a file but seeing what is happening is handy.

PowerShell Background Jobs

Receive-Job
Receive-Job’s actual purpose is to get the output from jobs at that point in time but by passing in the Wait parameter it will wait for the jobs to complete first. By default it will wait for all the jobs in the current session but you can pass in filters to wait for specific jobs. To me it looks like Receive-Job was not specifically designed as a mechanism to wait for job completion it is more a side effect when using the Wait parameter and it doesn’t have a timeout parameter.

PowerShell Background Jobs

You can also pass in the WriteEvents parameter that will print to the console as your jobs change state.

Handling Timeouts
Wait-Job takes a timeout parameter.

Wait-Job -Job $jobs -Timeout 60

If your jobs are not completed and the timeout is reached it will return from the Wait-Job call but it won’t show an error message. You’ll have to retrieve the jobs at this point and check which ones didn’t complete, you can use Get-Job for this.

PowerShell Background Jobs

Handling Errors
Errors that occur inside the script blocks running as a job won’t bubble up back to your script. If you use Receive-Job it will print to the console and if you use Wait-Job you won’t see anything, you’ll have to retrieve the jobs using Get-Job to see the failed status.

PowerShell Background Jobs

PowerShell Background Jobs

You can also store the errors that occur in each script block by using the ErrorVariable parameter, it is not a jobs specific feature it is one of the PowerShell CommonParameters, errors are stored in $Error automatic variable but by using ErrorVariable you can append multiple errors to the same variable and you can specify a different variable for each job to get the errors just for that job.

You can use Wait-Job and Receive-Job together by piping the result of Wait-Job to Receive-Job but that means you will only see the output from your jobs when they are done.

[Updated] Pass Parameters To Start-Job
As I mentioned earlier the job won’t have access to variables in your script, you can pass parameters to Start-Job using the Argumentlist parameter. It takes an array of arguments for example:

$param1 = @{"name" = "value" ; "name2" = "value2";}
$param2 = "value2"

$job = Start-Job -Name "job1" -ScriptBlock {
 param($param1, $param2)
 ...
} -ArgumentList @($param1, $param2)

Francois Delport