Hey everyone, this is another guest post from Ben Liebowitz, Matt’s brother.
Before I get into the script, I’ll give you some background… We are working on migrating VMs off of our vSphere 4.1 environment to our new vSphere 5.5 environment. The 4.1 environment is using the Nexus 1000v Virtual Switch while the 5.5 environment is using a vDS switch.
We found the easiest way to migrate the VMs was to remove them from inventory on the 4.1 side and then add them to inventory on the 5.5 side (as all the hosts in each environment saw the same storage). Doing this manually for approx 250 VMs was painful, so I scripted the process.
I started with exporting the VM’s information regarding its name, datastore location, Network Portgroup(s), & folder location. I found that by using the folder location in Get-VM, I was just getting it’s current folder name and not the full path. Being I have folder name duplicates (one in an Internal Folder and the same structure in a DMZ folder) I needed a way to list the full path. Thankfully, I found a Function written by Luc Dekens (@LucD22 / www.lucd.info) that he called #BlueFolderPath that solved my problem.
After that, I was able to connect to the 4.1 vCenter, export my list, shutdown the VM, remove it from inventory, then connect to my 5.5 vCenter, browse that same datastore, add the VM to inventory on the other side, move the VM to its original folder location, set the proper network portgroup, upgrade the virtual hardware, power the VM on, and finally upgrade the VMware tools.
———————————————-
# Define Variables $vcenter41 = “your existing vcenter” $vcenter55 = “your new vcenter” $vsphere55host = “one of the vSphere 5.5 hosts to migrate to” $datacenter = “datacenter name” $csvfile = “path to csv” #Build the BlueFolderPath function New-VIProperty -Name 'FullPath' -ObjectType 'VirtualMachine' -Value { param($vm) $current = Get-View $vm.ExtensionData.Parent $path = "" do { $parent = $current if($parent.Name -ne "vm"){$path = $parent.Name + "/" + $path} $current = Get-View $current.Parent } while ($current.Parent -ne $null) $path.TrimEnd('/') } -Force | Out-Null #Build the Get-folderbypath function function Get-FolderByPath { <# .SYNOPSIS Retrieve folders by giving a path .DESCRIPTION The function will retrieve a folder by it's path. The path can contain any type of leave (folder or datacenter). .NOTES Author: Luc Dekens .PARAMETER Path The path to the folder. This is a required parameter. .PARAMETER Separator The character that is used to separate the leaves in the path. The default is '/' .EXAMPLE PS> Get-FolderByPath -Path "Folder1/Datacenter/Folder2" .EXAMPLE PS> Get-FolderByPath -Path "Folder1>Folder2" -Separator '>' #> param( [CmdletBinding()] [parameter(Mandatory = $true)] [System.String[]]${Path}, [char]${Separator} = '/' ) process{ if((Get-PowerCLIConfiguration).DefaultVIServerMode -eq "Multiple"){ $vcs = $defaultVIServers } else{ $vcs = $defaultVIServers[0] } foreach($vc in $vcs){ foreach($strPath in $Path){ $root = Get-Folder -Name Datacenters -Server $vc $strPath.TrimStart($Separator).Split($Separator) | %{ $root = Get-Inventory -Name $_ -Location $root -Server $vc -NoRecursion if((Get-Inventory -Location $root -NoRecursion | Select -ExpandProperty Name) -contains "vm"){ $root = Get-Inventory -Name "vm" -Location $root -Server $vc -NoRecursion } } $root | where {$_ -is [VMware.VimAutomation.ViCore.Impl.V1.Inventory.FolderImpl]}|%{ Get-Folder -Name $_.Name -Location $root.Parent -Server $vc } } } } } # Enable connections to multiple vCenters Set-PowerCLIConfiguration -DefaultVIServerMode Multiple -Scope AllUsers -Confirm:$false # Connect to 4.1 vCenter connect-viserver vsphere41 # export list of VMs to Migrate to CSV Get-Datacenter -name "datacenter" | Get-VM | Select Name, @{N="Datastore";E={Get-Datastore -VM $_ | Select -ExpandProperty Name}}, @{N="Network";E={Get-VirtualPortgroup -VM $_ | Select -ExpandProperty Name}}, @{N="Parent";E={$_.FullPath}} | Export-Csv "c:\migrate.csv" -NoTypeinformation -UseCulture # Get VMs to Migrate $migrate = Get-Datacenter -name "datacenter" # Shutdown VMs (cleanly) $migrate | Get-VM | Shutdown-VMGuest -Confirm:$false # Wait for VMs to shutdown start-sleep -s 60 # Remove VMs from Inventory Get-datacenter -name "datacenter" | Get-Inventory | Remove-Inventory -Confirm:$false # Connect to 5.5 vCenter connect-viserver vsphere55 # Set a host in vcenter5.5 to add them to. $esxhost = "vsphere01.domain.local" ## Add backto inventory & Change network for VMs foreach ($row in (Import-Csv $csvfile)) { $vmName = $row.Name $vmxfile = "[$($row.Datastore)] $($vmName)/$($vmName).vmx" $vmNewVM = New-VM -VMFilePath $vmxfile -VMhost $esxhost # Move VM to existing folder locaiton Move-VM -VM $row.Name -Destination (get-folderbypath $row.Parent) # Change Network Portgroup $vm = get-vm -Name $row.Name Get-NetworkAdapter -VM $vm | Set-NetworkAdapter -NetworkName $row.Network -Confirm:$false # Upgrade Virtual Hardware Set-VM -VM $vmName -Version v10 # Poweron VMs Start-VM -VM $row.Name -Confirm:$false | wait-tools Get-VMQuestion -VM $vm | Set-VMQuestion –Option "I moved it" -confirm:$false # Update VMware Tools update-tools $vmName -Confirm:$false start-sleep -s 300 } ## end foreach
———————————————-
Enjoy!
Hi Matt,
I’m working on a script to migrate from vSphere 5.0 to 6.0 and came across yours which for the most part works (thank you BTW) but I’m seeing the following error when moving the VM to the destination folder. Thoughts?
Move-VM : Cannot convert ‘System.Object[]’ to the type ‘VMware.VimAutomation.ViCore.Types.V1.Inventory.VICo
ntainer’ required by parameter ‘Destination’. Specified method is not supported.
At C:\Users\****\VMWare\Scripts\Migration\migrate_guests.ps1:131 char:35
+ Move-VM -VM $row.Name -Destination <<<< (get-folderbypath $row.Parent)
+ CategoryInfo : InvalidArgument: (:) [Move-VM], ParameterBindingException
+ FullyQualifiedErrorId : CannotConvertArgument,VMware.VimAutomation.ViCore.Cmdlets.Commands.MoveVM
Regards,
Adrian