windowspowershellrunspace

How to call powershell ps1 file with argument in runspacepool with powershell module


I'm learning the powershell. Currently I have a tough requirement. I need to call an powershell script(ps1) in parallel from an powershell module(psm1). The ps1 task is like following

param(
    [Parameter(Mandatory=$true)]
    [String] $LogMsg,
    [Parameter(Mandatory=$true)]
    [String] $FilePath
)
Write-Output $LogMsg
$LogMsg | Out-File -FilePath $FilePath -Append

The FilePath is like "C:\Users\user\Documents\log\log1.log" And in the psm1 file, I use the runspacepool to do async task. Like the following demo

$MaxRunspaces = 5

$RunspacePool = [runspacefactory]::CreateRunspacePool(1, $MaxRunspaces)
$RunspacePool.Open()

$Jobs = New-Object System.Collections.ArrayList

Write-Host $currentPath
Write-Host $lcmCommonPath

$Filenames = @("log1.log", "log2.log", "log3.log")

foreach ($File in $Filenames) {
    Write-Host "Creating runspace for $File"
    $PowerShell = [powershell]::Create()
    $PowerShell.RunspacePool = $RunspacePool
    $FilePath = -Join("C:\Users\user\Documents\log\",$File)
    $PowerShell.AddScript("C:\Users\user\Documents\foo.ps1").AddArgument($FilePath) | Out-Null
    
    $JobObj = New-Object -TypeName PSObject -Property @{
        Runspace = $PowerShell.BeginInvoke()
        PowerShell = $PowerShell  
    }

    $Jobs.Add($JobObj) | Out-Null
}

But there are two serious problem.

  1. Can't pass the parameters to ps1 file. I just try to create the file path in the ps1 file side, it works and file created. But when I try to pass the argument from psm1 file. The files are not created. I also try to use script block and it can pass the parameters. But since my ps1 code is too large(The above is just part of it), using script block is unreal. I need a method to pass parameter to ps1 file.
  2. Can't get write-host information in ps1 file while psm1 is still running

If the runspacepool has limitation for passing the parameters to ps1 file, is there any other solution to deal with the async task for powershell script? Thanks.


Solution

  • Can't pass the parameters to ps1 file.

    Use AddParameter() instead of AddArgument() - this will allow you to bind the argument to a specific parameter by name:

    $PowerShell.AddScript("C:\Users\user\Documents\foo.ps1").
                AddParameter('FilePath', $FilePath).
                AddParameter('LogMsg', 'Log Message goes here') | Out-Null
    

    Can't get write-host information in ps1 file while psm1 is still running

    Correct - you cannot get host output from a script not attached to the host application's default runspace - but if you're using PowerShell 5 or newer you can collect the resulting information from the $PowerShell instance and relay that if you want to:

    # Register this event handler after creating `$PowerShell` but _before_ calling BeginInvoke()
    Register-ObjectEvent -InputObject $PowerShell.Streams.Information -EventName DataAdded -SourceIdentifier 'WriteHostRecorded' -Action {
      $recordIndex = $EventArgs.Index
      $data = $PowerShell.Streams.Information[$recordIndex]
      Write-Host "async task wrote '$data'"
    }