powershellparallel-processingworkflowfile-copying

Using PowerShell, can I copy one file from my local system to many remote computers in parallel?


As the title suggests, I cant seem to get either workflows or foreach-object -Parallel to work.

Here is one attempt:

$Computers = Get-Content 'C:\temp\Inputfiles\computers.txt'
$SourcePath = 'C:\Temp\Inputfiles\VMTools\Setup64.exe'

$Job = $Computers | ForEach-Object -Parallel {
  Copy-Item -Path $using:SourcePath -Destination "\\$_\C$\Temp\" -Force
  Start-Sleep -Seconds 1
} -ThrottleLimit 5 -AsJob

$Job | Wait-Job | Receive-Job

I tried variations of the above, but cant get it to work.

Here is the error I was getting on the above:

>  .\Copy-FileToRemote.ps1
ForEach-Object : Parameter set cannot be resolved using the specified named parameters.
At C:\Temp\ITScript\Workflow\Copy-FileToRemote.ps1:4 char:21
+ $Job = $Computers | ForEach-Object -Parallel {
+                     ~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : MetadataError: (:) [ForEach-Object], ParameterBindingException
    + FullyQualifiedErrorId : AmbiguousParameterSet,Microsoft.PowerShell.Commands.ForEachObjectCommand

Wait-Job : Cannot validate argument on parameter 'Job'. The argument is null or empty. Provide an argument that is not null or empty, and then try the command again.
At C:\Temp\ITScript\Workflow\Copy-FileToRemote.ps1:9 char:8
+ $Job | Wait-Job | Receive-Job
+        ~~~~~~~~
    + CategoryInfo          : InvalidData: (:) [Wait-Job], ParameterBindingValidationException
    + FullyQualifiedErrorId : ParameterArgumentValidationError,Microsoft.PowerShell.Commands.WaitJobCommand

Solution

  • Preface:


    Your solution options are:

    # Assumes that the ThreadJob module is available, which requires
    # on-demand installation in Windows PowerShell.
    
    $Computers = Get-Content 'C:\temp\Inputfiles\computers.txt'
    $SourcePath = 'C:\Temp\Inputfiles\VMTools\Setup64.exe'
    
    # Start a thread job for each computer.
    $Job = $Computers | ForEach-Object {
      Start-ThreadJob -ThrottleLimit 5 {
        Copy-Item -Path $using:SourcePath -Destination "\\$using:_\C$\Temp\" -Force
      }
    }
    
    $Job | Receive-Job -Wait -AutoRemoveJob
    

    Note how the caller's $_ value (the current pipeline input object) must in this case also be referenced via $using: - unlike with ForEach-Object -Parallel.


    Note:

    This answer originally showed an approach based on
    Copy-Item -ToSession; however, the -ToSession approach is generally not recommended: Thanks, noam.