proxb / PoshRSJob

Provides an alternative to PSjobs with greater performance and less overhead to run commands in the background, freeing up the console and allowing throttling on the jobs.
MIT License
541 stars 87 forks source link

Trying to loop cmdlets through Start-RSJob using the call operator fails #198

Closed powershellpr0mpt closed 5 years ago

powershellpr0mpt commented 5 years ago

Do you want to request a feature or report a bug? Report a bug

What is the current behavior? Using a call operator to call a cmdlet provides an error although I think it should work just fine according to the error message.

If the current behavior is a bug, please provide the steps to reproduce and if possible a minimal demo of the problem

$Computers = Get-Content C:\Temp\Computers.txt
$Commands = Get-Command -Module PSP-Inventory -Verb Get
foreach ($Command in $Commands){
   $Cmdlet = $Command.Name
   $CmdletNoun = $Cmdlet.Split('-')[1]

   $Computers | Start-RSJob -Scriptblock {& $Cmdlet -ComputerName $_} -Name {"{0} - {1}" -f $CmdletNoun,$_} -ModuleToImport PSP-Inventory | Wait-RSJob | Receive-RSJob -OutVariable $CmdletNoun
}

gives error message

The expression after '&' in a pipeline element produced an object that was not valid. It must result in a command name, a script block, or a CommandInfo object.

What is the expected behavior? For it to loop through all the Get- cmdlets and collect all the information to the appropriate variables

Which versions of Powershell and which OS are affected by this issue? Did this work in previous versions of our scripts? Tested on Windows PowerShell 5.1.14393.2758 [Desktop]

Please provide a code example showing the issue, if applicable:

#Code goes here

See earlier provided code sample. I've tried not using the Call operator [&], $Using:Cmdlet, the VariablesToImport parameter etc, but no luck

MVKozlov commented 5 years ago

What Module version you use ?

I just test $using:Cmdlet inside scriptblock and -VariablesToImport Cmdlet outside - it works

what if you insert Write-Host "Cmdlet: $Cmdlet" inside scriptblock ?

Start-RSJob -Verbose -Debug -VariablesToImport Cmdlet

...
VERBOSE: [VariablesToImport, System.String[]]
VERBOSE: Loading variables: Cmdlet
VERBOSE: Loaded variables: Cmdlet
...

Start-RSJob -Verbose -Debug with $using

...
DEBUG: Using AST with PowerShell V3+
DEBUG: CommandOrigin: Internal
VERBOSE: Found 1 $Using: variables!
DEBUG: NewScriptBlock: Param($_, $__using_Cmdlet)

      "Cmdlet: $($__using_Cmdlet)"
      & $__using_Cmdlet -ComputerName $_
...
powershellpr0mpt commented 5 years ago

Module version 1.7.4.4

when trying

$computers = 'localhost'
$Commands = Get-Command -Module PSP-Inventory -Verb Get
foreach ($Command in $Commands) {
    $Cmdlet = $Command.Name
    $CmdletNoun = $Cmdlet.Split('-')[1]

    $Computers | Start-RSJob -ScriptBlock {& $Using:Cmdlet -ComputerName $_} -ModulesToImport PSP-Inventory -VariablesToImport $Cmdlet | Wait-RSJob | Receive-RSJob -OutVariable $CmdletNoun
}

or

$computers = 'localhost'
$Commands = Get-Command -Module PSP-Inventory -Verb Get
foreach ($Command in $Commands) {
    $Cmdlet = $Command.Name
    $CmdletNoun = $Cmdlet.Split('-')[1]

    $Computers | Start-RSJob -ScriptBlock {Write-Host "Cmdlet: $Using:Cmdlet"} -ModulesToImport PSP-Inventory -VariablesToImport $Cmdlet -Verbose -Debug | Wait-RSJob | Receive-RSJob
}

I get

DEBUG: [BEGIN]
VERBOSE: Displaying PSBoundParameters
VERBOSE: [ScriptBlock, Write-Host "Cmdlet: $Using:Cmdlet"]
VERBOSE: [ModulesToImport, System.String[]]
VERBOSE: [VariablesToImport, System.String[]]
VERBOSE: [Verbose, True]
VERBOSE: [Debug, True]
VERBOSE: Creating default Job Name
VERBOSE: Loading variables: Get-DiskInfo
Get-Variable : Cannot find a variable with the name 'Get-DiskInfo'.
At C:\Program Files\WindowsPowerShell\Modules\poshrsjob\1.7.4.4\Public\Start-RSJob.ps1:294 char:31
+ ...         $vars = @(Get-Variable $varname -ErrorAction Continue | Where ...
+                       ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : ObjectNotFound: (Get-DiskInfo:String) [Get-Variable], ItemNotFoundException
    + FullyQualifiedErrorId : VariableNotFound,Microsoft.PowerShell.Commands.GetVariableCommand

DEBUG: ListCount: 0
DEBUG: [PROCESS]
DEBUG: [END]
DEBUG: ArgumentCount: 0 | List.Count: 1 | SBParamCount: 0 | InsertPSItemParam: True
VERBOSE: PowerShell Version: 5
DEBUG: NewScriptBlock: Param($_)
Write-Host "Cmdlet: $Using:Cmdlet"
VERBOSE: Creating new runspacepool <7fcebd45-1681-4621-8c3e-cd59c7be3081>
DEBUG: ListCount: 1
VERBOSE: Incrementing job ID
VERBOSE: Using localhost as pipeline variable
VERBOSE: Checking for Using: variables
VERBOSE: Checking for ArgumentList
VERBOSE: Invoking Runspace
VERBOSE: Determining Job Name
VERBOSE: Adding RSJob to Jobs queue
VERBOSE: Display RSJob
A Using variable cannot be retrieved. A Using variable can be used only with Invoke-Command, Start-Job, or InlineScript in the script workflow. When it is used with Invoke-Command, the Using variable is valid only if the script block is invoked on a remote computer.

for each cmdlet

MVKozlov commented 5 years ago
  1. You should not mix $using and -VariablesToImport at the same time
  2. When you use -VariablesToImport you should use 'variable' name (string) but not $variable itself
powershellpr0mpt commented 5 years ago

pfff, now that was dumb... So when I do $Cmdlet in the scriptblock and -VariablesToImport Cmdlet I get output

One thing that's very important in this case is in the test you used Write-Host , but that doesn't do anything

This proved working for me:

$Commands = Get-Command -Module PSP-Inventory -Verb Get
foreach ($Command in $Commands) {
    $Cmdlet = $Command.Name
    $CmdletNoun = $Cmdlet.Split('-')[1]

    Start-RSJob -ScriptBlock {write-output $Cmdlet} -VariablesToImport Cmdlet | Wait-RSJob | Receive-RSJob
}