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
Ok.
Try something like this.
$vmhost1 = "hostname1"
$vmhost2 = "hostname2"
$maxParallel = 2
$groups = Import-CSV -Path 'c:\Temp\migration\VMs_List-123.csv' -UseCulture |
Group-Object -Property vmhost
$i = $j = 0
$eof = $false
while(-not $eof){
if($i -lt $groups[0].Group.Count){
$vm = Get-VM -Name $groups[0].Group[$i].name
Move-VM -VM $vm -Datastore $ds -RunAsync
$i++
}
if($j -lt $groups[1].Group.Count){
$vm = Get-VM -Name $groups[1].Group[$j].name
Move-VM -VM $vm -Datastore $ds -RunAsync
$j++
}
while((Get-Task -Status Running | where{$_.Name -eq 'ApplyStorageDrsRecommendation_Task'}).Count -ge $maxParallel){
sleep 5
}
if($i -eq $groups[0].Group.Count -and $j -lt $groups[1].Group.Count){
$eof = $true
}
}
Blog: lucd.info Twitter: @LucD22 Co-author PowerCLI Reference
Is the number of VMs per VMHost in that CSV the same?
Blog: lucd.info Twitter: @LucD22 Co-author PowerCLI Reference
Thanks for the reply LucD!
For this one it is but it will probably won't be the same for other migrations.
Ok.
Try something like this.
$vmhost1 = "hostname1"
$vmhost2 = "hostname2"
$maxParallel = 2
$groups = Import-CSV -Path 'c:\Temp\migration\VMs_List-123.csv' -UseCulture |
Group-Object -Property vmhost
$i = $j = 0
$eof = $false
while(-not $eof){
if($i -lt $groups[0].Group.Count){
$vm = Get-VM -Name $groups[0].Group[$i].name
Move-VM -VM $vm -Datastore $ds -RunAsync
$i++
}
if($j -lt $groups[1].Group.Count){
$vm = Get-VM -Name $groups[1].Group[$j].name
Move-VM -VM $vm -Datastore $ds -RunAsync
$j++
}
while((Get-Task -Status Running | where{$_.Name -eq 'ApplyStorageDrsRecommendation_Task'}).Count -ge $maxParallel){
sleep 5
}
if($i -eq $groups[0].Group.Count -and $j -lt $groups[1].Group.Count){
$eof = $true
}
}
Blog: lucd.info Twitter: @LucD22 Co-author PowerCLI Reference
Thanks!
It did work like I wanted.
So let say I have more Hosts I can only add more groups?
Group[2], Group[3] and ect...
if($j -lt $groups[2].Group.Count){
$vm = Get-VM -Name $groups[2].Group[$i].name
Move-VM -VM $vm -Datastore $ds -RunAsync
$j++
You can, but you will have to add
sections per additional host.
And the maximum in parallel should preferably be the same as the number of hosts
Blog: lucd.info Twitter: @LucD22 Co-author PowerCLI Reference
Luc, sorry for the late reply.
I have an error message when reaching the eof.
At 1st I thought it was one of my VM that was at fault but it seems it every time it reach the it loops on the same VM on the file.
I added a write-host to see more what going on.
Currently moving VM VM-xLarge-01 from group 1 to the DatastoreCluster xxx-xxx
ApplyStorageDrsRecommendati... Running 0 03:38:56 PM
Currently moving VM Test-2 from group 2 to the DatastoreCluster xxx-xxx
ApplyStorageDrsRecommendati... Running 0 03:38:56 PM
Currently moving VM Test-2 from group 2 to the DatastoreCluster xxx-xxx
Move-VM : 2021-06-15 3:46:30 PM Move-VM A specified parameter was not correct: Must specify datastore on which to place disk already in pod
At C:\Users\xxx\Desktop\9_Move_VMs_To_Cluster.ps1:116 char:9
+ Move-VM -VM $vm -Datastore $ds -RunAsync
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [Move-VM], InvalidArgument
+ FullyQualifiedErrorId : Client20_VmServiceImpl_RelocateVmThroughSdrs_ViError,VMware.VimAutomation.ViCore.Cmdlets.Commands.MoveVM
Currently moving VM Test-2 from group 2 to the DatastoreCluster xxx-xxx
Move-VM : 2021-06-15 3:46:31 PM Move-VM A specified parameter was not correct: Must specify datastore on which to place disk already in pod
At C:\Users\xxx\Desktop\9_Move_VMs_To_Cluster.ps1:116 char:9
+ Move-VM -VM $vm -Datastore $ds -RunAsync
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [Move-VM], InvalidArgument
+ FullyQualifiedErrorId : Client20_VmServiceImpl_RelocateVmThroughSdrs_ViError,VMware.VimAutomation.ViCore.Cmdlets.Commands.MoveVM
Currently moving VM Test-2 from group 2 to the DatastoreCluster xxx-xxx
Move-VM : 2021-06-15 3:46:31 PM Move-VM A specified parameter was not correct: Must specify datastore on which to place disk already in pod
At C:\Users\xxx\Desktop\9_Move_VMs_To_Cluster.ps1:116 char:9
+ Move-VM -VM $vm -Datastore $ds -RunAsync
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [Move-VM], InvalidArgument
+ FullyQualifiedErrorId : Client20_VmServiceImpl_RelocateVmThroughSdrs_ViError,VMware.VimAutomation.ViCore.Cmdlets.Commands.MoveVM
Is that target datastore part of a datastorecluster?
Blog: lucd.info Twitter: @LucD22 Co-author PowerCLI Reference
Hi Luc,
Sorry for the delay, I've been testing it and found the issue.
while(-not $eof){
if($i -lt $groups[0].Group.Count){
$vm = Get-VM -Name $groups[0].Group[$i].name
write-host "Currently moving VM" $vm.name "from group 1 to the DatastoreCluster " $ds -ForegroundColor Green
Move-VM -VM $vm -Datastore $ds -RunAsync
$i++
}
if($j -lt $groups[1].Group.Count){
$vm = Get-VM -Name $groups[1].Group[$i].name
write-host "Currently moving VM" $vm.name "from group 2 to the DatastoreCluster " $ds -ForegroundColor Green
Move-VM -VM $vm -Datastore $ds -RunAsync
$j++
}
Group[0] and Group[1] had the same counter letter so that's why it kept looping on the same VM for group[1].
Thanks again for your help! Really appreciate it!
Cheers!
Well spotted, I corrected the code above.
Blog: lucd.info Twitter: @LucD22 Co-author PowerCLI Reference
Not sure if I need to open a new treat but...
If I want to shutdown the guest O/S before the migration and once the move is completed, power on the vm.
Is it possible to do in this script?
I can find out for the shutdown but to power it on after the task is completed I have no idea...
Thanks again!
That is possible, but it will make the script a bit more complex.
I'll use an ArrayList to keep track of the background jobs, and at the end of the script, it will show the vMotions that failed, if any.
$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) {
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.Name.PowerState -ne 'PoweredOff'){
sleep 5
}
$t = Move-VM -VM $vm -Datastore $ds -RunAsync
$TaskArr.Add($t.Id) | Out-Null
$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.Name.PowerState -ne 'PoweredOff') {
Start-Sleep 5
}
$t = Move-VM -VM $vm -Datastore $ds -RunAsync
$TaskArr.Add($t.Id) | Out-Null
$j++
}
while ((Get-Task -Status Running | Where-Object { $_.Name -eq 'ApplyStorageDrsRecommendation_Task' }).Count -ge $maxParallel) {
Start-Sleep 5
}
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)
}
if ($i -eq $groups[0].Group.Count -and $j -lt $groups[1].Group.Count) {
$eof = $true
}
}
while($TaskArr.Count -gt 0){
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)
}
$erroredOut = @()
Get-Task -Id $TaskArr.Id -Status Error -PipelineVariable tCompleted |
ForEach-Object -Process {
$erroredOut += $tCompleted.ExtensionData.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
Thanks Luc for this nice script.
I had to make a change to the while loop as it was looping forever.
Can you help me with this error, I get this when it is at the get-task:
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:133 char:16
+ Get-Task -Id $TaskArr.Id -Status Success -PipelineVariable tComplet ...
+ ~~~~~~~~~~~
+ CategoryInfo : InvalidData: (:) [Get-Task], ParameterBindingValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationError,VMware.VimAutomation.Common.Commands.Cmdlets.GetTask
Thank you again!
It could be that one or more Move-VM commands fail, then there is of course no TaskId.
You could build in a test (in both locations)
$t = Move-VM -VM $vm -Datastore $ds -RunAsync
if($t){
$TaskArr.Add($t.Id) | Out-Null
}
$i++
Did you check in the Web Client if there is a vMotion initiated for each VM?
Blog: lucd.info Twitter: @LucD22 Co-author PowerCLI Reference
Thanks for your quick reply.
I still have the same error with this new block of codes.
I can see on the web client that the Storage DRS has started.
I can see 4 running as I changed the max to 4, 2 for each hosts.
What exactly did you change in the While-loop?
I would need to run the script in a debugger, step by step, to further analyse what goes wrong.
Blog: lucd.info Twitter: @LucD22 Co-author PowerCLI Reference
the following as before it couldn't continue and the $vm variable was staying at "poweredON" like it was not updating the new value.
$eof = $false
while (-not $eof) {
if ($i -lt $groups[0].Group.Count) {
$vm = Get-VM -Name $groups[0].Group[$i].name
Shutdown-VMGuest -VM $vm -Confirm:$false
while($vm.PowerState -ne 'PoweredOff'){
write-host "$vm.name from Group 1" $vm.PowerState
$vm = Get-VM -Name $groups[0].Group[$i].name
sleep 15
}
If it can help, when I ran only the variable $t to see what going on I had this :
PS Q:\> $t
Name State % Complete Start Time Finish Time
---- ----- ---------- ---------- -----------
ApplyStorageDrsRecommendati... Running 0 12:45:18 PM
What was in the Id property of $t?
Do a $t.Id
When does this $eof change?
The VM powerstate should change, and that is why the script contains the line
$vm = Get-VM -Name $groups[0].Group[$i].name
Blog: lucd.info Twitter: @LucD22 Co-author PowerCLI Reference
What was in the Id property of $t?
Do a $t.Id
PS Q:\> $t.id
Task-task-7069
When does this $eof change?
Not sure I understand?
The VM powerstate should change, and that is why the script contains the line
$vm = Get-VM -Name $groups[0].Group[$i].name
Yes, but it seems it was just staying in the loop and not refreshing the variable.
I added some write-host to see if the script was looping but it seems to be stock in the while without going trough the $vm = Get-VM -Name $groups[0].Group[$i].name again to get it updated.
I'm still new to PS so I hope it doesn't sound to stupid.. :S
Thanks again for you quick replies!
Do all the Task object have a value for Id?
Check with
$TaskArr.Id
To exit the loop, the value of $eof must change at some point.
Where does that happen?
Blog: lucd.info Twitter: @LucD22 Co-author PowerCLI Reference