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
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 $_.