简体   繁体   中英

PWSH | Parallelize Invoke-VMScript

I'm trying to parallelize a portion of a bigger PWSH script, basically I need to cycle through a list of VMs and execute some commands on them, and to speed up the process, I need this to be executed in parallel:


#Utils.ps1 contains all the functions
.(".\src\utils.ps1")

# List of VMs on which we execute the space reclaim
$VMs = Get-Content .\vmnames.txt

# Check if directory ./Results exists, if not it will be created
CheckDirResults

# Check if dir temp exists, if not it will be created
CheckDirTemp

# Asks for vCenter credentials
GetVServerCred

# Asks for VM credentials
GetVMCred

# Connects to vCenter
ConnectVcenter

# Commands that will be executed on VMs on line 94 (Invoke-VMScript)
$InvokeScript = @'
#! /bin/bash
pwd
'@

foreach ($vm in $VMs) {
    $vms = Get-VM -Name $vm
    
    $vms | ForEach-Object -Parallel {
        Write-Output "Testing on $_"
        Invoke-VMScript -VM $_ -GuestCredential $VMCred -ScriptText $InvokeScript -Confirm:$false
    }
}

I also tried to simply execute the Invoke directly on $VMs like this:

$VMs | ForEach-Object -Parallel {
   Write-Output "Testing on $_"
   Invoke-VMScript -VM $_ -GuestCredential $VMCred -ScriptText $commands
}

In both cases, the Invoke-VMScript can't either connect to the VMs or can't find the chunk of code to execute on the VMs.

Errors:

"test.ps1" 44L, 934C                                                                                                                                                                                                                        36,0-1        81%
----                           ----  ----
evl6800756.sys.ntt.eu          443   VSPHERE.LOCAL\Administrator
Testing on euczdrpoc03
Testing on euczdrpoc30
Invoke-VMScript:
Line |
   3 |     Invoke-VMScript -VM $_ -GuestCredential $VMCred -ScriptText $comma …
     |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     | 1/17/2023 10:42:02 AM    Invoke-VMScript     You are not currently connected to any servers. Please connect first using a Connect cmdlet.
Invoke-VMScript:
Line |
   3 |     Invoke-VMScript -VM $_ -GuestCredential $VMCred -ScriptText $comma …
     |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     | 1/17/2023 10:42:02 AM    Invoke-VMScript     Value cannot be found for the mandatory parameter ScriptText
Invoke-VMScript:
Line |
   3 |     Invoke-VMScript -VM $_ -GuestCredential $VMCred -ScriptText $comma …
     |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     | 1/17/2023 10:42:02 AM    Invoke-VMScript     You are not currently connected to any servers. Please connect first using a Connect cmdlet.
Invoke-VMScript:
Line |
   3 |     Invoke-VMScript -VM $_ -GuestCredential $VMCred -ScriptText $comma …
     |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     | 1/17/2023 10:42:02 AM    Invoke-VMScript     Value cannot be found for the mandatory parameter ScriptText

The $using: scope modifier inside a -Parallel script-block lets you refer to variables assigned a value outside that script-block. Examples in your question are not complete enough to say for certain, but hopefully this explains at least some of the difficulty.

So for example within the parallel block, try $using:commands instead of your $commands . This bit from reference doc could be more helpfully spelled out, IMHO.

Use the $using: keyword to pass variable references to the running script.

That's demonstrated more clearly in one of the reference examples, in this blog post , and this answer .

I managed to do it this way:

# Commands that will be executed on VMs on line 94 (Invoke-VMScript)
$commands = @'
#! /bin/bash
hostname
'@

# Loads the VMs names from the file vmnames.txt
$vms = Get-Content ".\vmnames.txt"

# Uses the content of the file vmnames.txt to get the VM objects
$vmnames = Get-VM -Name $vms

# Executes the commands on the VMs
$vmnames | ForEach-Object -Parallel {
    # Loads the variables in the parallel session
    $commands = $using:commands
    $VMCred = $using:VMCred
    Invoke-VMScript -VM $_ -GuestCredential $VMCred -ScriptText $commands
}

For some reasons, piping directly $vms (so the content of the file to ForEach-Object -Parallel won't pass correctly the value (in this case the VM name), while using the Get-VM -Name $vms will do.

Then, once this is solved the ForEach-Object -Parallel will finally get the correct VM name assigned to $_.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM