I found this PowerCli script from LucD. It does work really well. 🙂
However I'm trying to find a way to run only 2 storage vMotions on 2 or more hosts reading a list of VMs for each host.
It does work but only 1 host at the time, no sure how to run to foreach at the same time or it will be an entire new way?
That is what I have so far, I've tried to add some if or else if but always ends up with the same results.
Any help or suggestion will be gladly appreciate. 🙂
Thanks,
$vmhost1 = "hostname1"
$vmhost2 = "hostname2"
$vmlist = Import-CSV c:\Temp\migration\VMs_List-123.csv
$vmlist1 = Import-CSV c:\Temp\migration\VMs_List-123.csv | where {$_.vmhost -eq $vmhost1}
$vmlist2 = Import-CSV c:\Temp\migration\VMs_List-123.csv | where {$_.vmhost -eq $vmhost2}
$maxParallel = 2
foreach ($VM1 in $VMlist1) {
write-host "Currently moving VM" $vm1.name "to the DatastoreCluster " $ds -ForegroundColor Green
Move-VM -VM (Get-VM -Name $vm1.name) -Datastore $ds -RunAsync
do {
sleep 5
}
while((Get-Task -Status Running | where{$_.Name -eq 'ApplyStorageDrsRecommendation_Task'}).Count -ge $maxParallel)
foreach ($VM2 in $VMlist2) {
write-host "Currently moving VM" $vm2.name "to the DatastoreCluster " $ds -ForegroundColor Green
Move-VM -VM (Get-VM -Name $vm2.name) -Datastore $ds -RunAsync
do {
sleep 5
}
while((Get-Task -Status Running | where{$_.Name -eq 'ApplyStorageDrsRecommendation_Task'}).Count -ge $maxParallel)
}
}
Disconnect-VIServer -Server * -Confirm:$false
$TaskArr.Id is returning nothing.
it exit the loop when the VM is shutdown and then going to the next group[1].
At the end of the script it is normal that $TaskArr is empty, but I would need to see what ir contains just before the error you are getting.
You could just add a couple of lines
$TaskArr.Count
$TaskArr.Id
Blog: lucd.info Twitter: @LucD22 Co-author PowerCLI Reference
The script never reach the end.
Once the vMotion is done it keep looping on the same error until I end it.
The VMs remain powerOff.
while ((Get-Task -Status Running | Where-Object { $_.Name -eq 'ApplyStorageDrsRecommendation_Task' }).Count -ge $maxParallel) {
Start-Sleep 5
}
Write-Host "TaskArr.Count =" $TaskArr.Count
Write-Host "TaskArr.ID =" $TaskArr.Id
****This where the error happen*****
Get-Task -id $TaskArr.id -Status Success -PipelineVariable tCompleted |
ForEach-Object -Process {
Get-VM -Name $tCompleted.ExtensionData.EntityName | Start-VM -Confirm:$false
$TaskArr.Remove($tCompleted)
}
*********************************start of the script
Target host: xxx-xx
State IPAddress OSFullName
----- --------- ----------
Running {xxx.xxx.xxx,... Microsoft Windows Server 2016 or later (64-...
VM-Large-02.name from Group 1 PoweredOn
VM-Large-02.name from Group 1 PoweredOn
Running {xxx.xxx.xxx,... Microsoft Windows Server 2016 or later (64-...
VM-Large-01.name from Group 2 PoweredOn
VM-Large-01.name from Group 2 PoweredOn
TaskArr.Count = 2
TaskArr.ID =
Get-Task : Cannot validate argument on parameter 'Id'. The argument is null or empty. Provide an argument that is not null or empty, and then try the command again.
At K:\DIMTPS 9\DIMTPS 9-2\Infrastructure\Software\Scripts\Migration\Work In Progress\Move_VMs_To_Cluster_PowerOn_PowerOff_RAW.ps1:150 char:16
+ Get-Task -id $TaskArr.id -Status Success -PipelineVariable tComplet ...
+ ~~~~~~~~~~~
+ CategoryInfo : InvalidData: (:) [Get-Task], ParameterBindingValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationError,VMware.VimAutomation.Common.Commands.Cmdlets.GetTask
Running {xxx.xxx.xxx} Microsoft Windows Server 2016 or later (64-...
GPNet-Test-01.name from Group 1 PoweredOn
GPNet-Test-01.name from Group 1 PoweredOn
Running {xxx.xxx.xxx} Microsoft Windows Server 2016 or later (64-...
GPNet-Test-02.name from Group 2 PoweredOn
GPNet-Test-02.name from Group 2 PoweredOn
TaskArr.Count = 4
TaskArr.ID =
Get-Task : Cannot validate argument on parameter 'Id'. The argument is null or empty. Provide an argument that is not null or empty, and then try the command again.
At K:\DIMTPS 9\DIMTPS 9-2\Infrastructure\Software\Scripts\Migration\Work In Progress\Move_VMs_To_Cluster_PowerOn_PowerOff_RAW.ps1:150 char:16
+ Get-Task -id $TaskArr.id -Status Success -PipelineVariable tComplet ...
+ ~~~~~~~~~~~
+ CategoryInfo : InvalidData: (:) [Get-Task], ParameterBindingValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationError,VMware.VimAutomation.Common.Commands.Cmdlets.GetTask
Thanks!
I think I found the error.
Where it says (twice)
$TaskArr.Add($t.Id) | Out-Null
it should say
$TaskArr.Add($t) | Out-Null
Try it with that change
Blog: lucd.info Twitter: @LucD22 Co-author PowerCLI Reference
Error on the same line but different message, also the TaskArr.ID is not empty anymore.
Target host: Dxx-xx
State IPAddress OSFullName
----- --------- ----------
Running {}
VM-Large-02.name from Group 1 PoweredOn
VM-Large-02.name from Group 1 PoweredOn
VM-Large-02.name from Group 1 PoweredOn
Running {131.137.251.123,... Microsoft Windows Server 2016 or later (64-...
VM-Large-01.name from Group 2 PoweredOn
VM-Large-01.name from Group 2 PoweredOn
TaskArr.Count = 2
TaskArr.ID = Task-task-7169 Task-task-7171
Get-Task : Parameter set cannot be resolved using the specified named parameters.
At K:\DIMTPS 9\DIMTPS 9-2\Infrastructure\Software\Scripts\Migration\Work In Progress\Move_VMs_To_Cluster_PowerOn_PowerOff_RAW.ps1:150 char:3
+ Get-Task -id $TaskArr.id -Status Success -PipelineVariable tComplet ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidArgument: (:) [Get-Task], ParameterBindingException
+ FullyQualifiedErrorId : AmbiguousParameterSet,VMware.VimAutomation.Common.Commands.Cmdlets.GetTask
Running {xxx.xxx.xxx.xxx} Microsoft Windows Server 2016 or later (64-...
GPNet-Test-01.name from Group 1 PoweredOn
GPNet-Test-01.name from Group 1 PoweredOn
GPNet-Test-01.name from Group 1 PoweredOn
Running {xxx.xxx.xxx.xxx} Microsoft Windows Server 2016 or later (64-...
GPNet-Test-02.name from Group 2 PoweredOn
GPNet-Test-02.name from Group 2 PoweredOn
That is my mistake.
The Get-Task cmdlet with the Id parameter does not take the Status parameter.
Change that line to
Get-Task -Id $TaskArr.Id -PipelineVariable $tCompleted | where{$_.State -eq 'Success'} |
Blog: lucd.info Twitter: @LucD22 Co-author PowerCLI Reference
Thanks Luc!
There is another error :
_______________________________________________________________________
TaskArr.Count = 4
TaskArr.ID = Task-task-7207 Task-task-7209 Task-task-7211 Task-task-7213
Get-VM : Cannot validate argument on parameter 'Name'. The argument is null or empty. Provide an argument that is not null or empty, and then try the command again.
At K:\DIMTPS 9\DIMTPS 9-2\Infrastructure\Software\Scripts\Migration\Work In Progress\Move_VMs_To_Cluster_PowerOn_PowerOff_RAW.ps1:152 char:18
+ Get-VM -Name $tCompleted.ExtensionData.EntityName | Start-VM -Con ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidData: (:) [Get-VM], ParameterBindingValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationError,VMware.VimAutomation.ViCore.Cmdlets.Commands.GetVM
_______________________________________________________________________
****************************************************************************************************************************************************************
while ((Get-Task -Status Running | Where-Object { $_.Name -eq 'ApplyStorageDrsRecommendation_Task' }).Count -ge $maxParallel) {
Start-Sleep 5
}
Write-Host "TaskArr.Count =" $TaskArr.Count
Write-Host "TaskArr.ID =" $TaskArr.Id
Get-Task -Id $TaskArr.Id -PipelineVariable $tCompleted | where{$_.State -eq 'Success'} |
ForEach-Object -Process {
Get-VM -Name $tCompleted.ExtensionData.EntityName | Start-VM -Confirm:$false <------- ON THIS LINE ***
$TaskArr.Remove($tCompleted)
}
That should be
$tCompleted.ExtensionData.Info.EntityName
Blog: lucd.info Twitter: @LucD22 Co-author PowerCLI Reference
I had some success task but the VMs didn't start.
I've try to figure it out but I can't. 😞
Get-VM : Cannot validate argument on parameter 'Name'. The argument is null or empty. Provide an argument that is not null or empty, and then try the command again.
At K:\DIMTPS 9\DIMTPS 9-2\Infrastructure\Software\Scripts\Migration\Work In Progress\Move_VMs_To_Cluster_PowerOn_PowerOff_RAW.ps1:152 char:18
+ Get-VM -Name $tCompleted.ExtensionData.Info.EntityName | Start-VM ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidData: (:) [Get-VM], ParameterBindingValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationError,VMware.VimAutomation.ViCore.Cmdlets.Commands.GetVM
Can you check what is in $tCompleted.ExtensionData.Info.EntityName
Blog: lucd.info Twitter: @LucD22 Co-author PowerCLI Reference
Nothing...
Is there anything under
$tCompleted.ExtensionData.Info
Blog: lucd.info Twitter: @LucD22 Co-author PowerCLI Reference
It returns nothing as well.
I updated the script with all the recent changes.
And I added a number of Write-Verbose lines for debugging.
Before running the following version, run $VerbosePreference = 'Continue'
$vmhost1 = "hostname1"
$vmhost2 = "hostname2"
$maxParallel = 2
[System.Collections.ArrayList]$TaskArr = @()
$groups = Import-Csv -Path 'c:\Temp\migration\VMs_List-123.csv' -UseCulture |
Group-Object -Property vmhost
$i = $j = 0
$eof = $false
while (-not $eof) {
Write-Verbose "EOF: $eof I: $i J:$j"
if ($i -lt $groups[0].Group.Count) {
$vm = Get-VM -Name $groups[0].Group[$i].name
Shutdown-VMGuest -VM $vm -Confirm:$false
while((Get-VM -Name $vm.PowerState -ne 'PoweredOff'){
sleep 5
}
$t = Move-VM -VM $vm -Datastore $ds -RunAsync
Write-Verbose "Move $($vm.Name) to datastore $($ds.Name) "
if($t){
$TaskArr.Add($t) | Out-Null
}
else{
Write-Verbose "Move Task is empty"
}
$i++
}
if ($j -lt $groups[1].Group.Count) {
$vm = Get-VM -Name $groups[1].Group[$j].name
Shutdown-VMGuest -VM $vm -Confirm:$false
while ((Get-VM -Name $vm.PowerState -ne 'PoweredOff') {
Start-Sleep 5
}
$t = Move-VM -VM $vm -Datastore $ds -RunAsync
Write-Verbose "Move $($vm.Name) to datastore $($ds.Name) "
if($t){
$TaskArr.Add($t) | Out-Null
}
else{
Write-Verbose "Move Task is empty"
}
$j++
}
Write-Verbose "EOF: $eof I: $i J:$j"
while ((Get-Task -Id $TaskArr.Id -PipelineVariable $tCompleted |
where{$_.State -eq 'Running' -and $_.Name -eq 'ApplyStorageDrsRecommendation_Task' }).Count -ge $maxParallel) {
Write-Verbose "Still background tasks running - going to sleep"
Start-Sleep 5
}
Write-Verbose "Checking for VMs to start"
Write-Verbose "Task in hash table $($TaskArr.Count)"
Get-Task -Id $TaskArr.Id -PipelineVariable $tCompleted | where{$_.State -eq 'Success'} |
ForEach-Object -Process {
Write-Verbose "Start VM $($tCompleted.ExtensionData.Info.EntityName)"
Get-VM -Name $tCompleted.ExtensionData.Info.EntityName | Start-VM -Confirm:$false
$TaskArr.Remove($tCompleted)
}
Write-Verbose "Task in hash table $($TaskArr.Count)"
if ($i -eq $groups[0].Group.Count -and $j -lt $groups[1].Group.Count) {
Write-Verbose "Reached the end of both VM lists"
$eof = $true
}
}
Write-Verbose "Wait till remaining Tasks are finished"
while($TaskArr.Count -gt 0){
Get-Task -Id $TaskArr.Id -PipelineVariable $tCompleted | where{$_.State -eq 'Success'} |
ForEach-Object -Process {
Write-Verbose "Start VM $($tCompleted.ExtensionData.Info.EntityName)"
Get-VM -Name $tCompleted.ExtensionData.Info.EntityName | Start-VM -Confirm:$false
$TaskArr.Remove($tCompleted)
}
$erroredOut = @()
Get-Task -Id $TaskArr.Id -PipelineVariable $tCompleted | where{$_.State -eq 'Error'} |
ForEach-Object -Process {
Write-Verbose "Error for VM $($tCompleted.ExtensionData.Info.EntityName)"
$erroredOut += $tCompleted.ExtensionData.Info.EntityName
$TaskArr.Remove($tCompleted)
}
}
if($erroredOut.Count -gt 0){
Write-Error "vMotion failed for VMs $($erroredOut -join ',')"
}
Blog: lucd.info Twitter: @LucD22 Co-author PowerCLI Reference
I corrected the code above (that typo appeared in 2 places).
What is the output you get when you use the -Verbose switch?
Blog: lucd.info Twitter: @LucD22 Co-author PowerCLI Reference
The Verbose switch don't show anything with this 1st error.
However I went ahead and change the code as mention in my previous reply.
This is the error message :
VERBOSE: 2021-07-26 2:22:31 PM Get-VM Finished execution
VERBOSE: 2021-07-26 2:22:46 PM Move-VM Finished execution
VERBOSE: Move VM-Large-01 to datastore DSC-FC-GEN-A
VERBOSE: EOF: False I: 1 J:1
Get-Task : 2021-07-26 2:22:46 PM Get-Task Object reference not set to an instance of an object.
At K:\DIMTPS 9\DIMTPS 9-2\Infrastructure\Software\Scripts\Migration\Work In Progress\Move_VMs_To_Cluster_PowerOn_PowerOff_Ver2.ps1:149 char:11
+ while ((Get-Task -Id $TaskArr.Id -PipelineVariable $tCompleted |
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [Get-Task], VimException
+ FullyQualifiedErrorId : Core_BaseCmdlet_UnknownError,VMware.VimAutomation.Common.Commands.Cmdlets.GetTask
VERBOSE: 2021-07-26 2:22:46 PM Get-Task Finished execution
VERBOSE: Checking for VMs to start
VERBOSE: Task in hash table 2
Get-Task : 2021-07-26 2:22:46 PM Get-Task Object reference not set to an instance of an object.
At K:\DIMTPS 9\DIMTPS 9-2\Infrastructure\Software\Scripts\Migration\Work In Progress\Move_VMs_To_Cluster_PowerOn_PowerOff_Ver2.ps1:157 char:3
+ Get-Task -Id $TaskArr.Id -PipelineVariable $tCompleted | where{$_.S ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [Get-Task], VimException
+ FullyQualifiedErrorId : Core_BaseCmdlet_UnknownError,VMware.VimAutomation.Common.Commands.Cmdlets.GetTask
In this version I added some more Write-Verbose line
$vmhost1 = "hostname1"
$vmhost2 = "hostname2"
$maxParallel = 2
[System.Collections.ArrayList]$TaskArr = @()
$groups = Import-Csv -Path 'c:\Temp\migration\VMs_List-123.csv' -UseCulture |
Group-Object -Property vmhost
$i = $j = 0
$eof = $false
while (-not $eof) {
Write-Verbose "EOF: $eof I: $i J:$j"
if ($i -lt $groups[0].Group.Count) {
$vm = Get-VM -Name $groups[0].Group[$i].name
Shutdown-VMGuest -VM $vm -Confirm:$false
while((Get-VM -Name $vm).PowerState -ne 'PoweredOff'){
sleep 5
}
$t = Move-VM -VM $vm -Datastore $ds -RunAsync
Write-Verbose "Move $($vm.Name) to datastore $($ds.Name) "
if($t){
Write-Verbose "Task created: $($t.Id)"
$TaskArr.Add($t) | Out-Null
Write-Verbose "TaskArr Count : $($TaskArr.Count)"
}
else{
Write-Verbose "Move Task is empty"
}
$i++
}
if ($j -lt $groups[1].Group.Count) {
$vm = Get-VM -Name $groups[1].Group[$j].name
Shutdown-VMGuest -VM $vm -Confirm:$false
while ((Get-VM -Name $vm).PowerState -ne 'PoweredOff') {
Start-Sleep 5
}
$t = Move-VM -VM $vm -Datastore $ds -RunAsync
Write-Verbose "Move $($vm.Name) to datastore $($ds.Name) "
if($t){
Write-Verbose "Task created: $($t.Id)"
$TaskArr.Add($t) | Out-Null
Write-Verbose "TaskArr Count : $($TaskArr.Count)"
}
else{
Write-Verbose "Move Task is empty"
}
$j++
}
Write-Verbose "EOF: $eof I: $i J:$j"
Write-Verbose "TaskArr Count : $($TaskArr.Count)"
while ((Get-Task -Id $TaskArr.Id -PipelineVariable $tCompleted |
where{$_.State -eq 'Running' -and $_.Name -eq 'ApplyStorageDrsRecommendation_Task' }).Count -ge $maxParallel) {
Write-Verbose "Still background tasks running - going to sleep"
Start-Sleep 5
}
Write-Verbose "Checking for VMs to start"
Write-Verbose "Task in hash table $($TaskArr.Count)"
Get-Task -Id $TaskArr.Id -PipelineVariable $tCompleted | where{$_.State -eq 'Success'} |
ForEach-Object -Process {
Write-Verbose "Start VM $($tCompleted.ExtensionData.Info.EntityName)"
Get-VM -Name $tCompleted.ExtensionData.Info.EntityName | Start-VM -Confirm:$false
$TaskArr.Remove($tCompleted)
}
Write-Verbose "Task in hash table $($TaskArr.Count)"
if ($i -eq $groups[0].Group.Count -and $j -lt $groups[1].Group.Count) {
Write-Verbose "Reached the end of both VM lists"
$eof = $true
}
}
Write-Verbose "Wait till remaining Tasks are finished"
while($TaskArr.Count -gt 0){
Get-Task -Id $TaskArr.Id -PipelineVariable $tCompleted | where{$_.State -eq 'Success'} |
ForEach-Object -Process {
Write-Verbose "Start VM $($tCompleted.ExtensionData.Info.EntityName)"
Get-VM -Name $tCompleted.ExtensionData.Info.EntityName | Start-VM -Confirm:$false
$TaskArr.Remove($tCompleted)
}
$erroredOut = @()
Get-Task -Id $TaskArr.Id -PipelineVariable $tCompleted | where{$_.State -eq 'Error'} |
ForEach-Object -Process {
Write-Verbose "Error for VM $($tCompleted.ExtensionData.Info.EntityName)"
$erroredOut += $tCompleted.ExtensionData.Info.EntityName
$TaskArr.Remove($tCompleted)
}
}
if($erroredOut.Count -gt 0){
Write-Error "vMotion failed for VMs $($erroredOut -join ',')"
}
s.
Perhaps this will provide more info
Blog: lucd.info Twitter: @LucD22 Co-author PowerCLI Reference
There was a typo in my last code
while((Get-VM -Name $vm.PowerState -ne 'PoweredOff'){
should have been
while((Get-VM -Name $vm).PowerState -ne 'PoweredOff'){
I corrected the code above.
PS: what happened to the CR in your reply?
That is hardly legible.
Blog: lucd.info Twitter: @LucD22 Co-author PowerCLI Reference