How to add Parameters into a Job using argument list - powershell

I'm trying to create a responsive form, using jobs, and the Add-Jobtracker matrix.
The problem that I have is when I try to call variables inside the job. I used the -Argumentlist parameter but it is always Null, and I'm sure that the way that I'm using it is wrong, could you please guide me with this?
This is just an example, as my original script is a little bit more complex, below script will only write text from 2 variables to a textbox. I've created the same outside and inside the jobscript, used argument list and param inside it and it didn't work.
$Var1 = "sdaasdsadsa"
$Var2 = "asdasdsadsadsa"
$JobScript = {
Param($Var1, $Var2)
Write-Host $Var1, $Var2
}
$UpdateScript = {
Param($Job)
$texbox.Text = 'Working...'
}
$CompletedScript = {
Param($Job)
$results = Receive-Job -Job $Job
$textbox.Text = $results
}
Add-JobTracker -Name "test" -JobScript $JobScript -UpdateScript $UpdateScript -CompletedScript $CompletedScript -ArgumentList $var1, $Var2
At this point it is not doing anything, I have an alternative code but it is longer than this one, and I did not want to bother you guys with a lot of lines.

The job is running under a different scope than the rest of your script, and in that scope the variables aren't defined and therefore $null.
To call variables inside a job, use the $Using:Varname syntax. Eg:
$Test = "TEST!"
$null = Start-Job {
$Test
}
$Job = Get-Job
Start-Sleep -Seconds 2
Receive-Job -Job $Job
Yields no output at all, but:
$Test = "TEST!"
$null = Start-Job {
$Using:Test
}
$Job = Get-Job
Start-Sleep -Seconds 2
Receive-Job -Job $Job
Yields:
TEST!
Read more about Remote Variables here: https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_remote_variables?view=powershell-6

Related

Pass command line arguments on to to "self" but with Start-Job

At [Asynchronous start][1] I had a question about starting a power-shell script asynchronously which creates a form. As answered in that question this can be solved using start-job
eg
Start-Job -ScriptBlock { test NW -NoWarning -Paranoia:2 }
So I have tried to write the Test.PS1 script routine so it re-calls itself with "Start-Job Test -NoSpawn" The switch nospawn then means it runs without a second call. I have tested this with the example code the above line now has to be and it works
Start-Job -ScriptBlock { test NW -NoSpawn -NoWarning -Paranoia:2 }
However I'm struggling to get the parameters from the original command line to passthrough to the job
I have tried creating a string in the correct format , an array , list the arguments manually , I either get repeated arguments being passed or all of the string ending up in the first Parameter $ComputerList -
A summary of the parameters and the attempts are
Param ([string]$ComputerList = 'status\edi.csv',[switch]$NoSpawn,[switch]$NoWarning,[switch]$Debug,[INT]$Paranoia=6)
...... <Snip>
Start-Job -ScriptBlock { test $ComputerList -NoSpawn -NoWarning:$NoWarning -Paranoia:$Paranoia }
Doesn't work due to scope - also switches are wrong way to do this
Start-Job -ScriptBlock { test -NoSpawn $Args } -argumentlist $ComputerList
Insufficent arguments but works - But I think One Argument is possible ?
Start-Job -ScriptBlock { test $Args -NoSpawn } -argumentlist #("-NoWarning:$NoWarning","-ComputerList:$ComputerList","-Paranoia:$Paranoia")
Everything ends up in $ComputerList
Start-Job -ScriptBlock { test $Args -NoSpawn } -argumentlist "-NoWarning:$NoWarning -ComputerList:$ComputerList -Paranoia:$Paranoia"
Everything ends up in $ComputerList
Full code follows
Param ([string]$ComputerList = 'status\edi.csv',[switch]$NoSpawn,[switch]$NoWarning,[switch]$Debug,[INT]$Paranoia=6)
$Log_Paranoia=$Paranoia
If ($Debug) { $debugPreference="Continue"} #enable debug messages if -debug is specified
If ($NoWarning) { $WarningPreference="SilentlyContinue"} #turn off warning messages
function Write-Paranoid($Level, $message) {
$CS=Get-PSCallStack
$Caller = $CS[1]
$Module = "$($Caller.FunctionName)[$($Caller.ScriptLineNumber)]"
$Diff=$level - $Log_Paranoia
$MSG= "$Module($($Level),$($Log_Paranoia)):$message"
if ($level - $Log_Paranoia -le 0 ) {
Write-host $MSG
}
if($Error.Count -gt 0 ) {
$MSG= "$Module($Level)ERROR:$Error[0]"
Write-Error $MSG
}
$error.clear()
}
Function AddStatusBar($form , $Txt) {
Write-Paranoid 10 "Enter"
$statusBar = New-Object System.Windows.Forms.StatusBar
$statusBar.DataBindings.DefaultDataSourceUpdateMode = 0
$statusBar.TabIndex = 4
$statusBar.Size = SDS 428 22
$statusBar.Location = SDP 0 337
$statusBar.Text = $Txt
$form.Controls.Add($statusBar)
$statusBar
Write-Paranoid 10 "Exit"
}
Function Create-Form ($Title)
{
Write-Paranoid 10 "Enter"
$form1 = New-Object System.Windows.Forms.Form
$form1.Text = $Title
$form1.DataBindings.DefaultDataSourceUpdateMode = 0
$form1.ClientSize = SDS 890 359
$form1.StartPosition = 0
$form1.BackColor = [System.Drawing.Color]::FromArgb(255,185,209,234)
$form1
Write-Paranoid 10 "Exit"
}
Function GenerateTestForm
{
Write-Paranoid 10 "Enter"
[reflection.assembly]::loadwithpartialname("System.Drawing") | Out-Null
[reflection.assembly]::loadwithpartialname("System.Windows.Forms") | Out-Null
$Form1 = Create-Form "Test Form"
$Alist = Get-CommandLine
$StatusBar = AddStatusBar $form1 $AList
$form1.ShowDialog() | Out-Null # Suspends calller
Write-Paranoid 10 "Exit"
}
if ($NoSpawn )
{
Write-Paranoid 3 " NoSpawn "
Write-Paranoid 5 "Call GenerateForm"
if ($Test) {
GenerateTestForm
} else {
GenerateTestForm
}
} else {
Write-Paranoid 3 "NOT NoSpawn restarting as job"
# note that test.ps1 is in the path so it will restart this script
# Start-Job -ScriptBlock { test $ComputerList -NoSpawn -NoWarning:$NoWarning -Paranoia:$Paranoia } #Wrong scope
# Start-Job -ScriptBlock { test -NoSpawn $Args } -argumentlist $ComputerList # Insufficent aruments but works - ONLY One Argument possible -
# Start-Job -ScriptBlock { test $Args -NoSpawn } -argumentlist #("-NoWarning:$NoWarning","-ComputerList:$ComputerList","-Paranoia:$Paranoia") # Everything ends up in $ComputerList
# Start-Job -ScriptBlock { test $Args -NoSpawn } -argumentlist "-NoWarning:$NoWarning -ComputerList:$ComputerList -Paranoia:$Paranoia" # Everything ends up in $ComputerList
}
Your problem can be reduced to this:
How can I re-invoke the script at hand as a background job, passing all original arguments (parameter values, including default parameter values) through?
A simplified example:
param (
[string] $ComputerList = 'status\edi.csv',
[switch] $NoSpawn,
[switch] $NoWarning,
[switch] $Debug,
[int] $Paranoia=6
)
if ($NoSpawn) { # already running as a background job.
"I'm now running in the background with the following arguments:"
$PSBoundParameters
} else { # must re-invoke via a background job
# Add *default* parameter values, if necessary, given that
# they're *not* reflected in $PSBoundParameters.
foreach ($paramName in $MyInvocation.MyCommand.Parameters.Keys) {
if (-not $PSBoundParameters.ContainsKey($paramName)) {
$defaultValue = Get-Variable -Scope Local -ValueOnly $paramName
if (-not ($null -eq $defaultValue -or ($defaultValue -is [switch] -and -not $defaultValue))) {
$PSBoundParameters[$paramName] = $defaultValue
}
}
}
# Start a background job that reinvokes this script with the original
# arguments / default values.
Start-Job {
$argsHash = $using:PSBoundParameters
& $using:PSCommandPath -NoSpawn #argsHash
} |
Receive-Job -Wait -AutoRemoveJob
}
Note:
For demonstration purposes, the initial call waits for the re-invocation via a background job to finish, using Receive-Job -Wait -AutoRemoveJob
In your real code, you can simply discard Start-Job's output (a job-information object) with $null = Start-Job { ... }, and then rely on the job getting cleaned up when the caller's session as a whole exits.
The extra code needed to propagate parameter default values is somewhat cumbersome, but necessary, given that the automatic $PSBoundParameters variable does not reflect default values.
GitHub issue #3285 discusses this limitation, and suggests a potential future solution.

Send string parameters to a Start-Job script block

I need to initialize a job using the shell. The job will be a delay plus a call to a vbScript. The following code works fine. For my example, the vbScript is just a single line with a MsgBox "Hello world!"
$functions = {
Function execute_vbs {
param ([string]$path_VBScript, [int]$secs)
Start-Sleep -Seconds $secs
cscript /nologo $path_VBScript
}
}
$seconds = 2
Start-Job -InitializationScript $functions -ScriptBlock {execute_vbs -path_VBScript 'C:\Users\[USERNAME]\Desktop\hello_world.vbs' -secs $seconds} -Name MyJob
The problem comes the moment I want to parameterize the vbScript path. (the idea is to do several different calls to some different vbScripts).
When I do this, the command seems to ignore the parameter input. I did other tests with int parameter and they work fine, the problem looks to be only with the string parameters. The following code does not work:
$functions = {
Function execute_vbs {
param ([string]$path_VBScript, [int]$secs)
Start-Sleep -Seconds $secs
cscript /nologo $path_VBScript
}
}
$input = 'C:\Users\[USERNAME]\Desktop\hello_world.vbs'
$seconds = 2
Start-Job -InitializationScript $functions -ScriptBlock {execute_vbs -path_VBScript $input -secs $seconds} -Name MyJob
I've also tried using the [-ArgumentList] command, but it has the same problem.
Any idea?
The problem is that the $input and $seconds variables inside your script block are in a different scope and are effectively different variables to the ones in the main script.
I've modified your script slightly to remove the call to VBScript to make it easier to reproduce here - my example code is:
$functions = {
Function execute_vbs {
param ([string]$path_VBScript, [int]$secs)
Start-Sleep -Seconds $secs
write-output "filename = '$path_VBScript'"
write-output "secs = '$secs'"
}
}
Here's two ways to fix it:
The Using: scope modifier
See https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_scopes?view=powershell-7#the-using-scope-modifier for the full details, but basically:
For any script or command that executes out of session, you need the Using scope modifier to embed variable values from the calling session scope, so that out of session code can access them.
$filename = 'C:\Users\[USERNAME]\Desktop\hello_world.vbs'
$seconds = 2
$job = Start-Job -InitializationScript $functions -ScriptBlock {
execute_vbs -path_VBScript $using:filename -secs $using:seconds
} -Name MyJob
wait-job $job
receive-job $job
# output:
# filename = 'C:\Users\[USERNAME]\Desktop\hello_world.vbs'
# secs = '2'
Note the $using before the variable names inside the script block - this allows you to "inject" the variables from your main script into the scriptblock.
ScriptBlock Parameters
You can define parameters on the script block similar to how you do it with a function, and then provide the values in the -ArgumentList parameter when you invoke Start-Job.
$filename = 'C:\Users\[USERNAME]\Desktop\hello_world.vbs'
$seconds = 2
$job = Start-Job -InitializationScript $functions -ScriptBlock {
param( [string] $f, [int] $s )
execute_vbs -path_VBScript $f -secs $s
} -ArgumentList #($filename, $seconds) -Name MyJob
wait-job $job
receive-job $job
# output:
# filename = 'C:\Users\[USERNAME]\Desktop\hello_world.vbs'
# secs = '2'
``

PowerShell - Lambda Functions Not Executing

This lambda function executes as expected:
$WriteServerName = {
param($server)
Write-Host $server
}
$server = "servername"
$WriteServerName.invoke($server)
servername
However, using the same syntax, the following script prompts for credentials and then exits to the command line (running like this: .\ScriptName.ps1 -ConfigFile Chef.config), implying that the lambda functions aren't executing properly (for testing, each should just output the server name).
Why does the former lambda function return the server name, but the ones in the script don't?
Param(
$ConfigFile
)
Function Main {
#Pre-reqs: get credential, load config from file, and define lambda functions.
$jobs = #()
$Credential = Get-Credential
$Username = $Credential.username
$ConvertedPassword = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($Credential.password)
$Password = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($ConvertedPassword)
$Config = Get-Content $ConfigFile -Raw | Out-String | Invoke-Expression
#Define lambda functions
$BootStrap = {
param($Server)
write-host $server
}
$RunChefClient = {
param($Server)
write-host $server
}
$SetEnvironment = {
param($Server)
write-host $server
}
#Create bootstrap job for each server and pass lambda functions to Scriptblock for execution.
if(($Username -ne $null) -and ($Password -ne $null))
{
ForEach($HashTable in $Config)
{
$Server = $HashTable.Server
$Roles = $HashTable.Roles
$Tags = $HashTable.Tags
$Environment = $HashTable.Environment
$ScriptBlock = {
param ($Server,$BootStrap,$RunChefClient,$SetEnvironment)
$BootStrap.invoke($Server)
$RunChefClient.invoke($Server)
$SetEnvironment.invoke($Server)
}
$Jobs += Start-Job -ScriptBlock $ScriptBlock -ArgumentList #($Server,$BootStrap,$RunChefClient,$SetEnvironment)
}
}
else {Write-Host "Username or password is missing, exiting..." -ForegroundColor Red; exit}
}
Main
Without testing, I am going to go ahead and say it's because you are putting your scriptblock executions in PowerShell Jobs and then not doing anything with them. When you start a job, it starts a new PowerShell instance and executes the code you give it along with the parameters you give it. Once it completes, the completed PSRemotingJob object sits there and does nothing until you actually do something with it.
In your code, all the jobs you start are assigned to the $Jobs variable. You can also get all your running jobs with Get-Job:
Get-Job -State Running
If you want to get any of the data returned from your jobs, you'll have to use Receive-Job
# Either
$Jobs | Receive-Job
# Or
Get-Job -State Running | Receive-Job

Powershell: get output from Receive-Job

I have a collection of jobs that are running. When they complete I use receive-job and it writes the output to the screen. I'd like to take that output and log it to a file. I don't want to tee the output produced while the jobs are running because with multiple jobs running at once the logging would be interspersed. Get-Job | Receive-Job prints the output in a nice organized manner.
I have tried all of the following and no output is written to the file or stored in a variable, it just goes to the screen:
#Wait for last job to complete
While (Get-Job -State "Running") {
Log "Running..." -v $info
Start-Sleep 10
}
Log ("Jobs Completed. Output:") -v $info
# Getting the information back from the jobs
foreach($job in Get-Job){
Receive-Job -Job $job | Out-File c:\Test.log -Append
$output = Receive-Job -Job $job
Log ("OUTPUT: "+$output)
Receive-Job -Job $job -OutVariable $foo
Log ("FOO: "+$foo)
}
EDIT:
I have removed the extra Receive-Job calls in the foreach to the following after seeing Keith's comment:
# Getting the information back from the jobs
foreach($job in Get-Job){
Receive-Job -Job $job -OutVariable temp
Write-Host ("Temp: "+$temp)
$temp | Out-File -FilePath c:\Test.log -Append
}
I also verified I'm not using Receive-Job anywhere else in the script. The write-host $temp and the out-file still produce no output though.
If the job uses Write-Host to produce output, Receive-Job returns $null, but the results get written to the host. However, if the job uses Write-Output to produce output in lieu of Write-Host, Receive-Job returns a string array [string[]] of the job output.
To demonstrate, enter this innocuous code at the PowerShell prompt:
$job = Start-Job -ScriptBlock {
[int] $counter = 0
while ($counter -lt 10) {
Write-Output "Counter = $counter."
Start-Sleep -Seconds 5
$counter++
}
}
Wait about 20-30 seconds for the job to produce some output, then enter this code:
$result = Receive-Job -Job $job
$result.Count
$result
$result | Get-Member
The $result object contains the strings produced by the job.
NOTE: After some experimentation I have found that using write-output or just outputting a variable directly is not a good solution either. If you have a function that uses either of those methods and also returns a value the output will be concatenated with the return value!
The best way I have found to log output from jobs is to use write-host in combination with the Start-Transcript and Stop-Transcript cmdlets. There are some disadvantages such as the ISE does not support Transcript cmdlets. Also I haven't found a way to output to the transcript without it going to the host (I wanted a robust log with a less verbose user experience), but it seems to be the best solution available currently. It is the only way I've been able to log concurrent jobs without interspersing the output without having to right multiple temporary log files for each job and then combining them.
Below was my previous answer that I left just for documentation reasons:
The issue here was not getting output from Receive-Job. The output from the jobs I was expecting to see is being written in the job by write-host. When receive-job was called I saw all my output on the screen, but nothing was available to pipe elsewhere. It seems when I call receive-job all those write-host cmdlets execute, but there's nothing for me to pick up and pipe to out-file. The following script demonstrates this. You'll notice "Sleepy Time" appears in both the log file and the host, while "Slept" only appears in the host. So, rather than trying to pipe Receive-Job to write-host, I need to modify my Log function to not use write-host or to split the output as shown below.
cls
rm c:\jobs.log
$foo = #(1,2,3)
$jobs = #()
foreach($num in $foo){
$s = { get-process -name "explorer"
Start-Sleep $args[0]
$x = ("Sleepy Time: "+$args[0])
write-host $x
$x
write-host ("Slept: "+$args[0])
}
$id = [System.Guid]::NewGuid()
$jobs += $id
Start-Job -Name $id -ScriptBlock $s -args $num
}
While (Get-Job -State "Running") {
cls
Get-Job
Start-Sleep 1
}
cls
Get-Job
write-host "Jobs completed, getting output"
foreach($job in $jobs){
Receive-Job -Name $job | out-file c:/jobs.log -append
}
Remove-Job *
notepad c:\jobs.log
Below is a sample that will run concurrent jobs and then log them sequentially as per mbourgon's request. You'll notice the timestamps indicate the work was interspersed, but the logging is displayed sequentially by job.
#This is an example of logging concurrent jobs sequentially
#It must be run from the powershell prompt as ISE does not support transcripts
cls
$logPath = "c:\jobs.log"
rm $logPath
Start-Transcript -Path $logPath -Append
#Define some stuff
$foo = #(0,1,2)
$bar = #(#(1,"AAA"),#(2,"BBB"),#(3,"CCC"))
$func = {
function DoWork1 {
Log "This is DoWork1"
return 0
}
function DoWork2 {
Log "This is DoWork2"
return 0
}
function Log {
Param([parameter(ValueFromPipeline=$true)]$InputObject)
$nl = [Environment]::NewLine.Chars(0)
$time = Get-Date -Format {HH:mm:ss}
Write-Host ($time+">"+$InputObject+$nl)
}
}
#Start here
foreach($num in $foo){
$s = { $num = $args[0]
$bar = $args[1]
$logPath = $args[2]
Log ("Num: "+$num)
for($i=0; $i -lt 5; $i++){
$out = ([string]::$i+$bar[$num][1])
Log $out
start-sleep 1
}
Start-Sleep $num
$x = DoWork1
Log ("The value of x is: "+$x)
$y = DoWork2
Log ("The value of y is: "+$y)
}
Start-Job -InitializationScript $func -ScriptBlock $s -args $num, $bar, $logPath
}
While (Get-Job -State "Running") {
cls
Get-Job
Start-Sleep 1
}
cls
Get-Job
write-host "Jobs completed, getting output"
Get-Job | Receive-Job
Remove-Job *
Stop-Transcript
notepad $logPath
I found soulution how to receive Write-Host results to variable with Receive-Job CMDlet, it's working on Powershell 5.1:
$var = Receive-Job $job 6>&1
I know this is old. I don't use jobs very often so I was obviously having the same problem when I stumbled on this thread. Anyway, I arrived at a different solution so I thought I'd throw it out there.
Also in PowerShell 5.1 I simply referenced the properties on the Job Object(s) In my case this looked like:
$Jobs.ChildJobs.Output | Out-File <LogFile> -Append
It looks like all the streams are recorded in this way. Simple echoes go to the Output, presumable the success stream. Write-Host commands go to Information Stream.
Output : {}
Error : {}
Progress : {}
Verbose : {}
Debug : {}
Warning : {}
Information : {}
When you receive the results of a job those results are deleted such that further calls to Receive-Job will have nothing to retrieve (assuming the job is completed). If you don't want Receive-Job to delete the results use the -Keep switch. Now this doesn't explain why your first call to Receive-Job isn't outputting anything to the log file ... unless there is a part of the script you're not showing that is doing a Receive-Job before this.
BTW when you use -OutVariable supply it what a variable name and not a variable e.g. -ov temp. Then you later access that info with $temp.
You could pipe your Get-Process to Format-Table, so you can see all of the output.
Get-Process -Name "explorer" | format-Table -hidetableheaders -AutoSize
And you could use something like this at the end of the script...
Get-job | Receive-Job 2>&1 >> c:\path\to\your\log\file.log
The "2>&1 >>" with grab all output from Get-Job | Receive-Job
A simple solution to the problem. Write-Verbose "blabla" -Verbose
$s = New-PSSession -ComputerName "Computer"
$j = Invoke-Command -Session $s -ScriptBlock { Write-Verbose "Message" -Verbose } -AsJob
Wait-Job $j
$Child = $j.ChildJobs[0]
$Child.Verbose | out-file d:/job.log -append
What I have done is to not call Receive-Job at all. But create a wrapper scriptblock that starts and stops a transcripts. Then in that wrapper script block I call the jobs script block like
&$ScriptBlock < Parameters >
So the transcript will capture all the output to a file.

Powershell: Call functions outside scriptblock

I was reading this post about getting functions passed into a scriptblock for use with jobs:
Powershell start-job -scriptblock cannot recognize the function defined in the same file?
I get how that works by passing the function in as variable and it works for the simple example. What about a real world solution though, is there a more elegant way of handling this?
I have script I'm using to deploy changes to vendor software. It reads an xml that tells it how to navigate the environment and performs the various tasks, ie: map drives, stop services, call a perl installation script. I would like to provide a parameter to the script to allow it to run concurrently, this way if the perl script takes 5 minutes (not uncommon) and you're rolling out to 11 servers you're not waiting for the script to run for an hour.
I'm just going to post some snippets since the full script is a little lengthy. A log function:
function Log
{
Param(
[parameter(ValueFromPipeline=$true)]
$InputObject,
[parameter()]
[alias("v")]
$verbosity = $debug
)
$messageIndex = [array]::IndexOf($verbosityArray, $verbosity)
$verbosityIndex = [array]::IndexOf($verbosityArray, $loggingVerbosity)
if($messageIndex -ge $VerbosityIndex)
{
switch($verbosity)
{
$debug {Write-Host $verbosity ": " $InputObject}
$info {Write-Host $verbosity ": " $InputObject}
$warn {Write-Host $verbosity ": " $InputObject -ForegroundColor yellow}
$error {Write-Host $verbosity ": " $InputObject -ForegroundColor red}
}
}
}
Here's another function that calls the log function:
function ExecuteRollout
{
param(
[parameter(Mandatory=$true)]
[alias("ses")]
$session,
[parameter(Mandatory=$true)]
$command
)
#invoke command
Invoke-Command -session $session -ScriptBlock {$res = cmd /v /k `"$args[0]`"} -args $command
#get the return code from the remote session
$res = Invoke-Command -session $session {$res}
Log ("Command Output: "+$res)
$res = [string] $res
$exitCode = $res.substring($res.IndexOf("ExitCode:"), 10)
$exitCode = $exitCode.substring(9,1)
Log ("Exit code: "+$exitCode)
return $exitCode
}
And lastly a snippet from my main so you can get an idea of what's going on. $target.Destinations.Destination will contain all the servers and relevant information about them that the deployment will go to. I removed some variable setup and logging to make this more compact so yes you'll see variables referenced that are never defined:
#Execute concurrently
$target.Destinations.Destination | %{
$ScriptBlock = {
$destination = $args[0]
Log -v $info ("Starting remote session on: "+$destination.Server)
$session = New-PSSession -computerName $destination.Server
$InitializeRemote -session $session -destination $destination
#Gets a little tricky here, we need to keep the cmd session so it doesn't lose the sys vars set by env.bat
#String everything together with &'s
$cmdString = $destDrive + ": & call "+$lesDestDir+"data\env.bat & cd "+$rolloutDir+" & perl ..\JDH-rollout-2010.pl "+$rollout+" NC,r:\les & echo ExitCode:!errorlevel!"
Log ("cmdString: "+$cmdString)
Log -v $info ("Please wait, executing the rollout now...")
$exitCode = $ExecuteRollout -session $session -command $cmdString
Log ("ExitCode: "+$exitCode)
#respond to return code from rollout script
$HandleExitCode -session $session -destination $destination -exitCode $exitCode
$CleanUpRemote -session $session -destination $destination
}
Start-Job $ScriptBlock -Args $_
}
So if i go with the approach in the link I'd be converting all my functions to variables and passing them in to the script block. Currently, my log function will by default log in DEBUG unless the verbosity parameter is explicitly passed as a different verbosity. If I convert my functins to variables however powershell doesn't seem to like this syntax:
$Log ("Print this to the log")
So I think I'd need to use the parameter all the time now:
$Log ("Print this to the log" -v $debug
So bottom line it looks like I just need to pass all my functions as variables to the script block and change some formatting when I call them. It's not a huge effort, but I'd like to know if there's a better way before I start hacking my script up. Thanks for the input and for looking, I know this is quite a long post.
I started another post about passing parameters to functions stored as variables, the answer to that also resolves this issue. That post can be found here:
Powershell: passing parameters to functions stored in variables
The short answer is you can use the initializationscript parameter of Start-Job to feed all your functions in if you wrap them in a block and store that in a variable.
Example:
# concurrency
$func = {
function Logx
{
param(
[parameter(ValueFromPipeline=$true)]
$msg
)
Write-Host ("OUT:"+$msg)
}
}
# Execution starts here
cls
$colors = #("red","blue","green")
$colors | %{
$scriptBlock =
{
Logx $args[0]
Start-Sleep 9
}
Write-Host "Processing: " $_
Start-Job -InitializationScript $func -scriptblock $scriptBlock -args $_
}
Get-Job
while(Get-Job -State "Running")
{
write-host "Running..."
Start-Sleep 2
}
# Output
Get-Job | Receive-Job
# Cleanup jobs
Remove-Job *