Customer called me to ask how they could easily migrate their existing vCenter to a new vCenter including all distributed vSwitches. They were moving from vCenter 4.0 to vCenter 4.1 and at the same time creating an AD intergrated vCenter and moving from physical to a virtual vCenter. The upgrade path would be a difficult road and they decided to just perform a fresh vCenter install. Since a dvSwitch is linked to vCenter and not the ESX host, you cannot move the dvSwitch to a new vCenter by connecting the ESX host to the new vCenter. We had to use a different path.
Biggest issue in this was the recreating of all the distributed virtual portgroups in the new vCenter, since they have over 400 different VLANs it would be a lot of manual work and of course error prone. Another issue was that the whole migration should take place without downtime for the VMs.
The migration path I suggested is found below. I searched for some PowerShell scripts to help automate this migration and found some great scripts from (of course) Luc Dekens (click for scripts) and Glenn Sizemore ( click for blog) that served as the basics for my migration scripts. The migration will be performed in a few steps. I haven’t automated all the steps and I created a number of small scripts instead of one big script to keep more control on the whole process.
Migration preparation
For this migration to work, you will need a ESX host with multiple vmnics. Since we’re going to move VMs from dvSwitch to vSwitch on the same host and can’t afford to lose network connectivity during these steps, it is important that you have a vmnic available for your existing dvSwitch and the new vSwitch. If your host has only a few vmnics, you might consider running on just one vmnic per (d)vSwitch during this migration. If your using Ethernet Channels, be sure to check with your network admins what the consequences are!!!
In this first version of my scripts, I only migrate the VLAN ID of the dvPortGroups. I’m working on a newer version that will migrate more dvPortGroups attributes like loadbalancing and security settings.
A global overview of the migration
- Create a standard vSwitch and portgroups with VLAN-IDs on the ESX host
- Change the portgroup of the VMs from dvPortGroup to the new temporary PortGroups
- Create the new dvSwitch plus dvPortGroups on the new vCenter
- Remove the dvSwitch from the ESX host
- Move the ESX host to the new vCenter
- Add the ESX host to the new dvSwitch
- Change the portgroup of the VMs from temporary PortGroup to the new dvPortGroups
Perform Migration
- Step01-Get-distributedswitch: This script reads the dvSwitches and dvPortGroups. Then the standard vSwitch will be created with the normal PortGroups and a CSV will be created holding a list of the PortGroups and their VLAN ID.
- Manual intervention: add one or more vmnics to the vswitch
- Step02-Move-VM-to-vSwitch: VMs will be migrated from the dvPortGroups to the temporary standard PortGroups
- Step03-create-dvswitch-dvportgroup: On the new vCenter the new dvSwitch and dvPortGroups will be created.
- Manual intervention: Remove the old dvSwitch from the ESX host
- Manual intervention: Move the ESX host to the new vCenter
- Manual intervention: Add the ESX host to the new dvSwitch
- Step04-Move-VM-to-dvSwitch : VMs will be migrated from the temporary PortGroups to the dvPortGroups
In the near future I will post some updates to these scripts and try to automate even more steps to make this a complete migration wizard. Feel free to comment with new ideas or issues you are running into. The powershell scripts below can be downloaded as ZIP: Migrate-dvSwitch-001
Step01-Get-DistributedSwitch:
# Migrating distributed vSwitch configurations from one vCenter to a new vCenter # Written by: Gabrie van Zanten # http://www.GabesVirtualWorld.com function Get-dvSwitch{ # This function was written by Luc Dekens # See: http://www.lucd.info/2009/10/12/dvswitch-scripting-part-2-dvportgroup/ param([parameter(Position = 0, Mandatory = $true)][string]$DatacenterName, [parameter(Position = 1, Mandatory = $true)][string]$dvSwitchName) $dcNetFolder = Get-View (Get-Datacenter $DatacenterName | Get-View).NetworkFolder $found = $null foreach($net in $dcNetFolder.ChildEntity){ if($net.Type -eq "VmwareDistributedVirtualSwitch"){ $temp = Get-View $net if($temp.Name -eq $dvSwitchName){ $found = $temp } } } $found } function Set-dvSwPgVLAN{ # This function was written by Luc Dekens # See: http://www.lucd.info/2009/10/12/dvswitch-scripting-part-2-dvportgroup/ param($dvSw, $dvPg, $vlanNr) $spec = New-Object VMware.Vim.DVPortgroupConfigSpec $spec.defaultPortConfig = New-Object VMware.Vim.VMwareDVSPortSetting $spec.DefaultPortConfig.vlan = New-Object VMware.Vim.VmwareDistributedVirtualSwitchVlanIdSpec $spec.defaultPortConfig.vlan.vlanId = $vlanNr $dvPg.UpdateViewData() $spec.ConfigVersion = $dvPg.Config.ConfigVersion $taskMoRef = $dvPg.ReconfigureDVPortgroup_Task($spec) $task = Get-View $taskMoRef while("running","queued" -contains $task.Info.State){ $task.UpdateViewData("Info") } } function Get-dvSwPg{ param($dvSw ) # Search for Portgroups $dvSw.Portgroup | %{Get-View -Id $_} } # Report will be used to store the changes $report=@() # Ask what vCenter, datacenter and dvSwitch we're talking about Disconnect-VIServer "*" -Force:$true $vCenterOld = Read-Host "What is the name of the OLD vCenter: " $DatacenterName = Read-Host "What is the name of the datacenter on the OLD vCenter: " $OlddvSwitch = Read-Host "What is the name of the distributed vSwitch (dvSwitch) you want to move: " $MovingHost = Read-Host "What is the name of the host you want to move: " # Make the connection to vCenter Connect-VIServer -Server $vCenterOld # Read the dvSwitch and dvPortGroups $dvSwitch = Get-dvSwitch $DatacenterName $OlddvSwitch $dvPG = Get-dvSwPg $dvSwitch # Now create a (temporary) standard vSwitch with 128 ports. Remember, each VM needs one port and 128 might not be enough for you. # The name of this temporary standard vSwitch will be 'vSwitch-Migrate' New-VirtualSwitch -Name 'vSwitch-Migrate' -NumPorts 128 -VMHost $MovingHost $vSwitch = Get-VirtualSwitch -VMHost $MovingHost -Name 'vSwitch-Migrate' foreach( $dvPGroup in $dvPG ) { $VLANID = $dvPGroup.Config.DefaultPortConfig.Vlan.VlanId # Somehow the first line in $dvPGroup is some kind of header with 'VMware.Vim.NumericRange' in it. So I skip it if( $VLANID -notmatch 'VMware.Vim.NumericRange') { # I want the new standard Portgroup to be named 'Mig-VLAN100' instead of 'VLAN100' $NewPG = 'Mig-' + $dvPGroup.Name # Create a New standard portgroup Get-VirtualSwitch -VMHost $MovingHost -Name 'vSwitch-Migrate' | New-VirtualPortGroup -Name $NewPG -VLanId $VLANID # Just to always know what was what, I keep track of old and new names # This is where you could add more settings from the olddvPG, like load balancing, number of ports, etc. $Conversion = "" | Select olddvSwitch, olddvPG, tmpvSwitch, tmpvPG, VLANID $Conversion.olddvswitch = $dvSwitch.Name $Conversion.olddvPG = $dvPGroup.Name $Conversion.tmpvSwitch = Get-VirtualSwitch -VMHost $MovingHost -Name 'vSwitch-Migrate' $Conversion.tmpvPG = $NewPG $Conversion.VLANID = $VLANID $report += $Conversion } } # Writing the info to CSV file $report | Export-Csv "d:\csv\switch-list.csv" -NoTypeInformation Write-Host "The dvSwitch and dvPortGroups have been exported to CSV file and a standard vSwitch with portgroups has been created." Write-Host "Next Step is to run script 'Step02-Move-VM-to-vSwitch' which will move all VMs to standard vSwitches and portgroups"
Step02-Move-VM-to-vSwitch:
# Migrating distributed vSwitch configurations from one vCenter to a new vCenter # Written by: Gabrie van Zanten # http://www.GabesVirtualWorld.com <# This function has been written by Glenn Sizemore http://get-admin.com/blog/ Code at: http://poshcode.org/1895 .SYNOPSIS Retrieves the virtual network adapters available on a vSphere server. .DESCRIPTION A replacement for the default cmdlet will work with either a standard vSwitch or a dvSwitch. Retrieves the virtual network adapters available on a vSphere server. .PARAMETER VM Specify the virtual machines from which you want to retrieve virtual network adapters. .EXAMPLE Get-VM | Get-dvSwitchNetworkAdapter .OUTPUTS PSObject .LINK Set-dvSwitchNetworkAdapter #> Function Get-dvSwitchNetworkAdapter { [cmdletbinding()] param( [parameter(Mandatory = $true, ValueFromPipeline = $true)] [VMware.VimAutomation.Client20.VirtualMachineImpl[]] $VM ) process { Foreach ($VirtualMachine in (get-view -VIObject $VM -Property "Config.Hardware.Device","Network")) { $VirtualVmxnets = $VirtualMachine.Config.Hardware.Device | Where-Object {"VirtualEthernetCard","VirtualVmxnet" -contains $_.GetType().BaseType.Name} Foreach ($VirtualVmxnet in $VirtualVmxnets) { New-Object PSObject -Property @{ MacAddress = $VirtualVmxnet.MacAddress WakeOnLanEnabled = $VirtualVmxnet.WakeOnLanEnabled NetworkName = switch ($VirtualVmxnet.Backing.GetType().Name) { "VirtualEthernetCardNetworkBackingInfo" { Get-View $VirtualVmxnet.Backing.network -Property 'Name'| Select-Object -ExpandProperty 'Name' } Default { Get-view -ID $VirtualMachine.Network -Property 'Name','Key' | Where-Object {$_.Key -eq $VirtualVmxnet.Backing.Port.PortgroupKey} | Select-Object -ExpandProperty 'Name' } } Type = $VirtualVmxnet.GetType().Name ParentId = $VirtualMachine.MoRef.Type + "-" + $VirtualMachine.MoRef.Value ConnectionState = New-Object PSObject -Property @{ Connected = $VirtualVmxnet.Connectable.Connected StartConnected = $VirtualVmxnet.Connectable.StartConnected } Id = "{0}-{1}/{2}" -f $VirtualMachine.MoRef.Type,$VirtualMachine.MoRef.Value, $VirtualVmxnet.Key Name = $VirtualVmxnet.DeviceInfo.Label } } } } } <# This function has been written by Glenn Sizemore http://get-admin.com/blog/ Code at: http://poshcode.org/1895 .SYNOPSIS Changes the configuration of the virtual network adapter. .DESCRIPTION A replacement for the default cmdlet will work with either a standard vSwitch or a dvSwitch. Changes the configuration of the virtual network adapter. .PARAMETER NetworkAdapter Specify the name of the network to which you want to connect the virtual network adapter. NetworkName must be globally unique within vSphere. .PARAMETER NetworkName Specify the virtual network adapter you want to configure. .PARAMETER StartConnected If the value is $true, the virtual network adapter starts connected when its associated virtual machine powers on. If the value is $false, it starts disconnected. If Nothing is Supplied than nothing will be changed. .PARAMETER Connected If the value is $true, the virtual network adapter is connected after its creation. If the value is $false, it is disconnected. If Nothing is Supplied than nothing will be changed. .RunAsync If supplied then the cmdlet will not wait for the completion of the network reconfiguration. Instead a Task object will be returned and the cmdlet will process the next object in the pipeline. .EXAMPLE Get-VM | Get-dvSwitchNetworkAdapter | Where-object{$_.NetworkName -eq "192.168.1.0"}| Set-dvSwitchNetworkAdapter -NetworkName "dvSwitch0_192.168.1.0" .EXAMPLE Get-VM | Get-dvSwitchNetworkAdapter | Set-dvSwitchNetworkAdapter -StartConnected $True .OUTPUTS PSObject .LINK Get-dvSwitchNetworkAdapter #> Function Set-dvSwitchNetworkAdapter { [cmdletbinding(SupportsShouldProcess=$true)] param( [parameter(Mandatory = $true, ValueFromPipeline = $true)] [PSCustomObject[]] $NetworkAdapter , [parameter(Mandatory = $false)] [string] $NetworkName , [parameter()] [bool] $StartConnected , [parameter()] [bool] $Connected , [parameter()] [switch] $RunAsync ) process { Foreach ($netAdapter in $NetworkAdapter) { $VirtualMachine = Get-View $netAdapter.ParentId -Property 'Runtime.Host','Config.Hardware.Device','Name' $HostSystem = Get-View -ID $VirtualMachine.Runtime.Host -Property 'Network','Name' $VirtualDeviceConfigSpec = New-Object VMware.Vim.VirtualDeviceConfigSpec $VirtualDeviceConfigSpec.operation = "edit" Try { $VirtualDeviceConfigSpec.device = $VirtualMachine.Config.Hardware.Device | Where-Object {$_.GetType().Name -eq $netAdapter.Type -and $_.MacAddress -eq $netAdapter.MacAddress} } Catch { write-warning "$($netAdapter.Name) not found on $($VirtualMachine.name)" break; } $Msg = "" if ($NetworkName) { $Network = Get-View -Id $HostSystem.Network -Property 'Name','Config' | Where-Object { $_.Name -eq $NetworkName } if ($Network) { $Msg += "Connecting $($netAdapter.Name) to $($NetworkName) " # Determine backing type switch($Network.GetType().Name) { "Network" { $VirtualDeviceConfigSpec.device.backing = New-Object VMware.Vim.VirtualEthernetCardNetworkBackingInfo $VirtualDeviceConfigSpec.device.backing.deviceName = $NetworkName } "DistributedVirtualPortgroup" { $VirtualDeviceConfigSpec.device.Backing = New-Object VMware.Vim.VirtualEthernetCardDistributedVirtualPortBackingInfo $VirtualDeviceConfigSpec.device.backing.port = New-Object VMware.Vim.DistributedVirtualSwitchPortConnection $VirtualDeviceConfigSpec.device.backing.port.switchUuid = (Get-View $Network.Config.DistributedVirtualSwitch).Uuid $VirtualDeviceConfigSpec.device.backing.port.portgroupKey = $Network.Config.Key } } } Else { Write-Warning "$($NetworkName) was not found on $($HostSystem.Name)" } } if ($PSCmdlet.MyInvocation.BoundParameters.Connected -ne $null) { $Msg += "Connected:$($Connected) " $VirtualDeviceConfigSpec.Device.Connectable.Connected = $Connected } Else { $VirtualDeviceConfigSpec.Device.Connectable.Connected = $netAdapter.ConnectionState.Connected } if ($PSCmdlet.MyInvocation.BoundParameters.StartConnected -ne $null) { $Msg += "StartConnected:$($StartConnected) " $VirtualDeviceConfigSpec.Device.Connectable.StartConnected = $StartConnected } Else { $VirtualDeviceConfigSpec.Device.Connectable.StartConnected = $netAdapter.ConnectionState.StartConnected } $VirtualMachineConfigSpec = New-Object VMware.Vim.VirtualMachineConfigSpec $VirtualMachineConfigSpec.deviceChange += $VirtualDeviceConfigSpec IF ($PSCmdlet.ShouldProcess($VirtualMachine.Name,$Msg)) { $Task = Get-VIObjectByVIView -MORef $VirtualMachine.ReconfigVM_Task($VirtualMachineConfigSpec) if (-Not $RunAsync) { Wait-Task -Task $Task | Out-Null $returnNetworkAdapter = $netAdapter $returnNetworkAdapter.NetworkName = $NetworkName $returnNetworkAdapter.ConnectionState.StartConnected = $VirtualDeviceConfigSpec.Device.Connectable.StartConnected $returnNetworkAdapter.ConnectionState.Connected = $VirtualDeviceConfigSpec.Device.Connectable.Connected $returnNetworkAdapter } else { $task } } } } } # Now impport the csv file with the switch info $report = Import-Csv "d:\csv\switch-list.csv" Foreach( $row in $report) { # Set all VMs where dvPortGroup is equal to olddvPG to new standard (temporary) tmpvPG Write-Host "Switching VMs from dvPortGroups to standard PortGroup: " $row.olddvPG $row.tmpvPG Get-VM | Get-dvSwitchNetworkAdapter | Where-object{$_.NetworkName -eq $row.olddvPG }| Set-dvSwitchNetworkAdapter -NetworkName $row.tmpvPG } Write-Host "All VMs have now been moved from the dvSwitches to the standard vSwitches." Write-Host "You can now remove this host from the old vCenter and add it to the new vCenter" Write-Host "If the dvSwitch and dvPortGroups have not yet been created on the new vCenter," Write-Host "you should first run 03-Create-New-dvPG.ps1" Write-Host "If the dvSwitch and dvPortGroups have been created on the new vCenter," Write-Host "run 04-Move-VMs-to-dvPG.ps1"
Step03-create-dvswitch-dvportgroup:
# Migrating distributed vSwitch configurations from one vCenter to a new vCenter # Written by: Gabrie van Zanten # http://www.GabesVirtualWorld.com function New-dvSwitch{ param($dcName, $dvSwName, $baseUplink, $nrUplink) $dc = Get-View -ViewType Datacenter -Filter @{"Name"=$dcName} $net = Get-View -Id $dc.NetworkFolder $spec = New-Object VMware.Vim.DVSCreateSpec $spec.configSpec = New-Object VMware.Vim.DVSConfigSpec $spec.configspec.name = $dvSwName $spec.configspec.uplinkPortPolicy = New-Object VMware.Vim.DVSNameArrayUplinkPortPolicy $spec.configspec.uplinkPortPolicy.UplinkPortName = (1..$nrUplink | % {$baseUplink + $_}) $taskMoRef = $net.CreateDVS_Task($spec) $task = Get-View $taskMoRef while("running","queued" -contains $task.Info.State) { $task.UpdateViewData("Info") } $task.Info.Result } function Get-dvSwitch{ param([parameter(Position = 0, Mandatory = $true)][string]$DatacenterName, [parameter(Position = 1, Mandatory = $true)][string]$dvSwitchName) $dcNetFolder = Get-View (Get-Datacenter $DatacenterName | Get-View).NetworkFolder $found = $null foreach($net in $dcNetFolder.ChildEntity){ if($net.Type -eq "VmwareDistributedVirtualSwitch"){ $temp = Get-View $net if($temp.Name -eq $dvSwitchName){ $found = $temp } } } $found } function New-dvSwPortgroup{ # This function was written by Luc Dekens # See: http://www.lucd.info/2009/10/12/dvswitch-scripting-part-2-dvportgroup/ # As you can see, many parameters have been included in the function, but are not being used when creating the dvPortGroup # You can change the script to your likings, make sure the extra items you want to import are exported in Step01-Get-DistributedSwitch.ps1 param([parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $true)][VMware.Vim.VmwareDistributedVirtualSwitch]$dvSw, [parameter(Position = 1, Mandatory = $true)][string]$PgName, [int]$PgNumberPorts = 64, [string]$PgBinding = "earlyBinding", [string]$PgVLANType = "none", [int[]]$PgVLANId, [switch]$SecPolPromiciousMode = $false, [switch]$SecPolMacChanges = $true, [switch]$SecPolForgedTransmits = $true, [switch]$TeamingCheckDuplex = $false, [switch]$TeamingCheckErrorPercent = $false, [string]$TeamingCheckSpeed = $false, [switch]$TeamingFullDuplex = $true, [int]$TeamingPercentage, [int]$TeamingSpeed, [string]$TeamingPolicy = "loadbalance_srcid", [switch]$TeamingNotifySwitches = $true, [switch]$TeamingRollingOrder = $false, [switch]$TeamingReversePolicy = $true, [string[]]$TeamingActiveUplink, [string[]]$TeamingStandbyUplink ) process{ $teamingPolicies = "loadbalance_ip", "loadbalance_srcmac", "loadbalance_srcid", "failover_explicit", "loadbalance_loadbased" $spec = New-Object VMware.Vim.DVPortgroupConfigSpec $spec.Name = $PgName $spec.Type = $PgBinding $spec.numPorts = $PgNumberPorts $spec.defaultPortConfig = New-Object VMware.Vim.VMwareDVSPortSetting switch($PgVLANType.ToLower()){ "vlan" { $spec.defaultPortConfig.VLAN = New-Object VMware.Vim.VmwareDistributedVirtualSwitchVlanIdSpec $spec.defaultPortConfig.VLAN.vlanId = $PgVLANId[0] } "vlan trunking" { $spec.defaultPortConfig.VLAN = New-Object VMware.Vim.VmwareDistributedVirtualSwitchTrunkVlanSpec $spec.defaultPortConfig.VLAN.vlanId = Get-VLANRanges $PgVLANId } "private vlan" { $spec.defaultPortConfig.VLAN = New-Object VMware.Vim.VmwareDistributedVirtualSwitchPvlanSpec $spec.defaultPortConfig.VLAN.pvlanId = $PgVLANId[0] } Default{} } $spec.defaultPortConfig.securityPolicy = New-Object VMware.Vim.DVSSecurityPolicy $spec.defaultPortConfig.securityPolicy.allowPromiscuous = New-Object VMware.Vim.BoolPolicy $spec.defaultPortConfig.securityPolicy.allowPromiscuous.Value = $SecPolPromiciousMode $spec.defaultPortConfig.securityPolicy.forgedTransmits = New-Object VMware.Vim.BoolPolicy $spec.defaultPortConfig.securityPolicy.forgedTransmits.Value = $SecPolForgedTransmits $spec.defaultPortConfig.securityPolicy.macChanges = New-Object VMware.Vim.BoolPolicy $spec.defaultPortConfig.securityPolicy.macChanges.Value = $SecPolMacChanges $spec.defaultPortConfig.uplinkTeamingPolicy = New-Object VMware.Vim.VmwareUplinkPortTeamingPolicy $spec.defaultPortConfig.uplinkTeamingPolicy.failureCriteria = New-Object VMware.Vim.DVSFailureCriteria if($TeamingCheckDuplex){ $spec.defaultPortConfig.uplinkTeamingPolicy.failureCriteria.checkDuplex = $TeamingCheckDuplex $spec.defaultPortConfig.uplinkTeamingPolicy.failureCriteria.fullDuplex = $TeamingFullDuplex } if($TeamingCheckErrorPercent){ $spec.defaultPortConfig.uplinkTeamingPolicy.failureCriteria.checkErrorPercent = $TeamingCheckErrorPercent $spec.defaultPortConfig.uplinkTeamingPolicy.failureCriteria.percentage = $TeamingPercentage } if("exact","minimum" -contains $TeamingCheckSpeed){ $spec.defaultPortConfig.uplinkTeamingPolicy.failureCriteria.checkSpeed = $TeamingCheckSpeed $spec.defaultPortConfig.uplinkTeamingPolicy.failureCriteria.speed = $TeamingSpeed } $spec.defaultPortConfig.uplinkTeamingPolicy.notifySwitches = New-Object VMware.Vim.BoolPolicy $spec.defaultPortConfig.uplinkTeamingPolicy.notifySwitches.Value = $TeamingNotifySwitches if($teamingPolicies -contains $TeamingPolicy){ $spec.defaultPortConfig.uplinkTeamingPolicy.policy = New-Object VMware.Vim.StringPolicy $spec.defaultPortConfig.uplinkTeamingPolicy.policy.Value = $TeamingPolicy } $spec.defaultPortConfig.uplinkTeamingPolicy.reversePolicy = New-Object VMware.Vim.BoolPolicy $spec.defaultPortConfig.uplinkTeamingPolicy.reversePolicy.Value = $TeamingReversePolicy $spec.defaultPortConfig.uplinkTeamingPolicy.rollingOrder = New-Object VMware.Vim.BoolPolicy $spec.defaultPortConfig.uplinkTeamingPolicy.rollingOrder.Value = $TeamingRollingOrder $spec.defaultPortConfig.uplinkTeamingPolicy.uplinkPortOrder = New-Object VMware.Vim.VMwareUplinkPortOrderPolicy $spec.defaultPortConfig.uplinkTeamingPolicy.uplinkPortOrder.activeUplinkPort = $TeamingActiveUplink $spec.defaultPortConfig.uplinkTeamingPolicy.uplinkPortOrder.standbyUplinkPort = $TeamingStandbyUplink $taskMoRef = $dvSw.AddDVPortgroup_Task($spec) $task = Get-View $taskMoRef while("running","queued" -contains $task.Info.State){ $task.UpdateViewData("Info") } $task.Info.Result } } # In this step the new dvSwitches will be created on the new vCenter $vCenterNew = Read-Host "What is the name of the new vCenter: " $DatacenterName = Read-Host "What is the name of the datacenter: " Disconnect-VIServer "*" -Force:$true Connect-VIServer -Server $vCenterNew # Reading CSV file $report = Import-Csv "d:\csv\switch-list.csv" | Select olddvSwitch -Unique foreach( $row in $report) { Write-Host "Creating dvSwitch: " $row.olddvSwitch # Creating the new dvSwitch. You may want to change the baseUplink and nrUplink values to your needs New-dvSwitch -dcName $DatacenterName -dvSwName $row.olddvSwitch -baseUplink 1 -nrUplink 1 } # dvPortGroups will be created on the new vCenter # Reading CSV file $report = Import-Csv "d:\csv\switch-list.csv" foreach( $row in $report) { $dvSwitchFromCSV = Get-dvSwitch dc02 $row.olddvSwitch Write-Host "Creating dvPortGroup " $row.olddvPG " with VLANID " $row.VLANID " on dvSwitch " $row.olddvSwitch New-dvSwPortgroup -dvSw $dvSwitchFromCSV -PgName $row.olddvPG -PgVLANType "vlan" -PgVLANId $row.VLANID } Write-Host "The new dvSwitch and dvPortGroups have been created" Write-Host "You can now move your ESX host with running VMs from the old to the new vCenter" Write-Host "Don't forget to add your ESX host to the newly created dvSwitch on the new vCenter !!!" Write-Host "Next step is to switch the VMs over to the dvPortGroups. Use 'Step04-Move-VM-to-dvSwitch' for this."
Step04-Move-VM-to-dvSwitch:
# Migrating distributed vSwitch configurations from one vCenter to a new vCenter # Written by: Gabrie van Zanten # http://www.GabesVirtualWorld.com <# This function has been written by Glenn Sizemore http://get-admin.com/blog/ Code at: http://poshcode.org/1895 .SYNOPSIS Retrieves the virtual network adapters available on a vSphere server. .DESCRIPTION A replacement for the default cmdlet will work with either a standard vSwitch or a dvSwitch. Retrieves the virtual network adapters available on a vSphere server. .PARAMETER VM Specify the virtual machines from which you want to retrieve virtual network adapters. .EXAMPLE Get-VM | Get-dvSwitchNetworkAdapter .OUTPUTS PSObject .LINK Set-dvSwitchNetworkAdapter #> Function Get-dvSwitchNetworkAdapter { [cmdletbinding()] param( [parameter(Mandatory = $true, ValueFromPipeline = $true)] [VMware.VimAutomation.Client20.VirtualMachineImpl[]] $VM ) process { Foreach ($VirtualMachine in (get-view -VIObject $VM -Property "Config.Hardware.Device","Network")) { $VirtualVmxnets = $VirtualMachine.Config.Hardware.Device | Where-Object {"VirtualEthernetCard","VirtualVmxnet" -contains $_.GetType().BaseType.Name} Foreach ($VirtualVmxnet in $VirtualVmxnets) { New-Object PSObject -Property @{ MacAddress = $VirtualVmxnet.MacAddress WakeOnLanEnabled = $VirtualVmxnet.WakeOnLanEnabled NetworkName = switch ($VirtualVmxnet.Backing.GetType().Name) { "VirtualEthernetCardNetworkBackingInfo" { Get-View $VirtualVmxnet.Backing.network -Property 'Name'| Select-Object -ExpandProperty 'Name' } Default { Get-view -ID $VirtualMachine.Network -Property 'Name','Key' | Where-Object {$_.Key -eq $VirtualVmxnet.Backing.Port.PortgroupKey} | Select-Object -ExpandProperty 'Name' } } Type = $VirtualVmxnet.GetType().Name ParentId = $VirtualMachine.MoRef.Type + "-" + $VirtualMachine.MoRef.Value ConnectionState = New-Object PSObject -Property @{ Connected = $VirtualVmxnet.Connectable.Connected StartConnected = $VirtualVmxnet.Connectable.StartConnected } Id = "{0}-{1}/{2}" -f $VirtualMachine.MoRef.Type,$VirtualMachine.MoRef.Value, $VirtualVmxnet.Key Name = $VirtualVmxnet.DeviceInfo.Label } } } } } <# This function has been written by Glenn Sizemore http://get-admin.com/blog/ Code at: http://poshcode.org/1895 .SYNOPSIS Changes the configuration of the virtual network adapter. .DESCRIPTION A replacement for the default cmdlet will work with either a standard vSwitch or a dvSwitch. Changes the configuration of the virtual network adapter. .PARAMETER NetworkAdapter Specify the name of the network to which you want to connect the virtual network adapter. NetworkName must be globally unique within vSphere. .PARAMETER NetworkName Specify the virtual network adapter you want to configure. .PARAMETER StartConnected If the value is $true, the virtual network adapter starts connected when its associated virtual machine powers on. If the value is $false, it starts disconnected. If Nothing is Supplied than nothing will be changed. .PARAMETER Connected If the value is $true, the virtual network adapter is connected after its creation. If the value is $false, it is disconnected. If Nothing is Supplied than nothing will be changed. .RunAsync If supplied then the cmdlet will not wait for the completion of the network reconfiguration. Instead a Task object will be returned and the cmdlet will process the next object in the pipeline. .EXAMPLE Get-VM | Get-dvSwitchNetworkAdapter | Where-object{$_.NetworkName -eq "192.168.1.0"}| Set-dvSwitchNetworkAdapter -NetworkName "dvSwitch0_192.168.1.0" .EXAMPLE Get-VM | Get-dvSwitchNetworkAdapter | Set-dvSwitchNetworkAdapter -StartConnected $True .OUTPUTS PSObject .LINK Get-dvSwitchNetworkAdapter #> Function Set-dvSwitchNetworkAdapter { [cmdletbinding(SupportsShouldProcess=$true)] param( [parameter(Mandatory = $true, ValueFromPipeline = $true)] [PSCustomObject[]] $NetworkAdapter , [parameter(Mandatory = $false)] [string] $NetworkName , [parameter()] [bool] $StartConnected , [parameter()] [bool] $Connected , [parameter()] [switch] $RunAsync ) process { Foreach ($netAdapter in $NetworkAdapter) { $VirtualMachine = Get-View $netAdapter.ParentId -Property 'Runtime.Host','Config.Hardware.Device','Name' $HostSystem = Get-View -ID $VirtualMachine.Runtime.Host -Property 'Network','Name' $VirtualDeviceConfigSpec = New-Object VMware.Vim.VirtualDeviceConfigSpec $VirtualDeviceConfigSpec.operation = "edit" Try { $VirtualDeviceConfigSpec.device = $VirtualMachine.Config.Hardware.Device | Where-Object {$_.GetType().Name -eq $netAdapter.Type -and $_.MacAddress -eq $netAdapter.MacAddress} } Catch { write-warning "$($netAdapter.Name) not found on $($VirtualMachine.name)" break; } $Msg = "" if ($NetworkName) { $Network = Get-View -Id $HostSystem.Network -Property 'Name','Config' | Where-Object { $_.Name -eq $NetworkName } if ($Network) { $Msg += "Connecting $($netAdapter.Name) to $($NetworkName) " # Determine backing type switch($Network.GetType().Name) { "Network" { $VirtualDeviceConfigSpec.device.backing = New-Object VMware.Vim.VirtualEthernetCardNetworkBackingInfo $VirtualDeviceConfigSpec.device.backing.deviceName = $NetworkName } "DistributedVirtualPortgroup" { $VirtualDeviceConfigSpec.device.Backing = New-Object VMware.Vim.VirtualEthernetCardDistributedVirtualPortBackingInfo $VirtualDeviceConfigSpec.device.backing.port = New-Object VMware.Vim.DistributedVirtualSwitchPortConnection $VirtualDeviceConfigSpec.device.backing.port.switchUuid = (Get-View $Network.Config.DistributedVirtualSwitch).Uuid $VirtualDeviceConfigSpec.device.backing.port.portgroupKey = $Network.Config.Key } } } Else { Write-Warning "$($NetworkName) was not found on $($HostSystem.Name)" } } if ($PSCmdlet.MyInvocation.BoundParameters.Connected -ne $null) { $Msg += "Connected:$($Connected) " $VirtualDeviceConfigSpec.Device.Connectable.Connected = $Connected } Else { $VirtualDeviceConfigSpec.Device.Connectable.Connected = $netAdapter.ConnectionState.Connected } if ($PSCmdlet.MyInvocation.BoundParameters.StartConnected -ne $null) { $Msg += "StartConnected:$($StartConnected) " $VirtualDeviceConfigSpec.Device.Connectable.StartConnected = $StartConnected } Else { $VirtualDeviceConfigSpec.Device.Connectable.StartConnected = $netAdapter.ConnectionState.StartConnected } $VirtualMachineConfigSpec = New-Object VMware.Vim.VirtualMachineConfigSpec $VirtualMachineConfigSpec.deviceChange += $VirtualDeviceConfigSpec IF ($PSCmdlet.ShouldProcess($VirtualMachine.Name,$Msg)) { $Task = Get-VIObjectByVIView -MORef $VirtualMachine.ReconfigVM_Task($VirtualMachineConfigSpec) if (-Not $RunAsync) { Wait-Task -Task $Task | Out-Null $returnNetworkAdapter = $netAdapter $returnNetworkAdapter.NetworkName = $NetworkName $returnNetworkAdapter.ConnectionState.StartConnected = $VirtualDeviceConfigSpec.Device.Connectable.StartConnected $returnNetworkAdapter.ConnectionState.Connected = $VirtualDeviceConfigSpec.Device.Connectable.Connected $returnNetworkAdapter } else { $task } } } } } # Now impport the csv file with the switch info $report = Import-Csv "d:\csv\switch-list.csv" Foreach( $row in $report) { # Set all VMs where dvPortGroup is equal to tmpvPG to it's original portgroup olddvPG Write-Host "Switching VMs from temporary PortGroup " $row.tmpvPG " to dvPortGroup " $row.olddvPG Get-VM | Get-dvSwitchNetworkAdapter | Where-object{$_.NetworkName -eq $row.tmpvPG }| Set-dvSwitchNetworkAdapter -NetworkName $row.olddvPG } Write-Host "All VMs have now been moved from the dvSwitches to the standard vSwitches."
Very nice Gabe. That may come in handy in the near future for me.
Are the vDS’ stored in the vCenter DB? Wouldn’t it be easier to migrate that and just reattach the new vCenter server? I may have to try this in my vLab. And the new inventory snapshot fling, and a million other things, cause all I have is free-time… Nice write up tho, will definitely be of use, thanks!
@dboftlp:twitter
Has anyone been able to run these scripts succesfully? I tried script 1 and it worked, script 2 doesn’t look like it has a host connection method? I suppose it should run against the host you are moving, not vCenter? Script 3 looks like it’s hard coded to a data center called “dc02”. Script 2 fails on line 153, property ‘config’ not found… Maybe my environment is very different? It’s vCenter 4.1, ESXi 4.1
Assuming you had some regular vSwitches for management, and that your physical vCenter was already at version 5, couldn’t you just build a new virtual vCenter server and connect it to the same database that was used by the old physical server?
My understanding is that the vDS is stored in the database, so it should get the config from there and everything should just come back after the switch.
This sounds much easier than the above solution.
Any problem with this?
Jim
Continuing previous comment…
======================
Optional – change vmnic failover for portgroup names Change-dvUplink-Failover.ps1
=====================
$DVGroup_mgmt =Read-Host “please provide portgroup name for management portgroup”
# Add custom cmdlets for vdspowercli
Add-PSSnapin VMware.VimAutomation.VdsComponent -ErrorAction SilentlyContinue
# Disconnect from vcenters to clean up sesssion
if ($global:DefaultVIServers.Count -gt 0)
{
Write-Host “Disconnecting vcenter sessions from previous connection/script..”
Disconnect-VIServer * -Force:$true -Confirm:$false
}
$vCenterNew = Read-Host “What is the name of the new vCenter”
Write-Host “Connecting to the new vCenter…”
Start-Sleep -Seconds 1
Connect-VIServer -Server $vCenterNew
#select datacenter
Get-Datacenter | Select-object @{E={$_.Name};L=”Datacenters found:”} | format-table |out-default
$DatacenterName = Read-Host “What is the name of the datacenter on the NEW vCenter? ”
# Select vDS to update
Get-VDS | Select @{E={$_.Name};L=”DVS Switch found in $vCenterNew:”;} | ft |out-default
$VdsSwitchName = Read-Host “What is the name of the distributed vSwitch you want to update?”
$VdsSwitch = Get-VDS -Name $VdsSwitchName
# Define portgroups
Write-Host “Script parameters show DVGroup name as “$DVGroup_mgmt
Write-Host “Press any key if you are happy to continue or Ctl-C to abort the script”
$x = $Host.UI.RawUI.ReadKey(“NoEcho,IncludeKeyDown”)
$pg_substring = Read-Host “Please provide portgroup substring, e.g. ‘SYSARCH’. Hit Enter if you want to process all portgroups”
$VdsSwitch
| Get-VdsDistributedPortGroup | Where-Object{($_.Name -notmatch
$DVGroup_mgmt) -and ($_.Name -match $pg_substring)} | Select Name |
out-default
Write-Host “Management portgroup:”
$VdsSwitch | Get-VdsDistributedPortGroup | Where-Object{$_.Name -match $DVGroup_mgmt} | Select Name | out-default
$DVGroup_all
= $VdsSwitch | Get-VdsDistributedPortGroup | Where-Object{($_.Name
-notmatch $DVGroup_mgmt) -and ($_.Name -match $pg_substring)}
$DVGroup_mgmt = $VdsSwitch | Get-VdsDistributedPortGroup | Where-Object{$_.Name -match $DVGroup_mgmt}
# Demote All dvportgroups , excluding management
$yes = New-Object System.Management.Automation.Host.ChoiceDescription “&Yes”,””
$no = New-Object System.Management.Automation.Host.ChoiceDescription “&No”,””
$choices = [System.Management.Automation.Host.ChoiceDescription[]]($yes,$no)
$caption = “Demotion of dvUplink”
$message = “If you want to demote dvUplink on all dvUplinks excluding management, please answer YES.”
$result = $Host.UI.PromptForChoice($caption,$message,$choices,0)
if($result -eq 0) {
Write-Host
“Please provide dvUplink name you would like to keep in ACTIVE mode,
i.e. ‘dvUplink1’.This will move second dvUPlink to Unused state.”
$active_dvuplink = Read-Host “Active dvUplink name”
Write-Host “I am about to demote second vnic to Unused on all portgroups you selected, exsluding management adapters”
Write-Host “Press any key to continue or Ctl-C to abort the script”
$x = $Host.UI.RawUI.ReadKey(“NoEcho,IncludeKeyDown”)
$VdsSwitch | Get-VdsDistributedPortGroup -Name $DVGroup_all | Set-VdsDVPortTeamingPolicy -ActiveUplink $active_dvuplink
}
# Promote all dvgroups , excluding managemet
$yes = New-Object System.Management.Automation.Host.ChoiceDescription “&Yes”,””
$no = New-Object System.Management.Automation.Host.ChoiceDescription “&No”,””
$choices = [System.Management.Automation.Host.ChoiceDescription[]]($yes,$no)
$caption = “Promotion of dvUplink”
$message = “If you want to promote dvUplink, please answer YES.”
$result = $Host.UI.PromptForChoice($caption,$message,$choices,0)
if($result -eq 0) {
Write-Host
“Please provide dvUplink name you would like to keep in ACTIVE mode,
i.e. ‘dvUplink1′.This will move second dvUPlink to Unused state.”
Write-Host “Since are adding dvUplinks to dvswitch and need to have several uplinks in Active mode,please provied both”
$active_dvuplink1 = Read-Host “First active dvUplink”
$active_dvuplink2 = Read-Host “First second dvUplink”
Write-Host “I am about to promote dvUplinks on all portgroups you selected, exsluding management adapters”
Write-Host “Press any key to continue or Ctl-C to abort the script”
$x = $Host.UI.RawUI.ReadKey(“NoEcho,IncludeKeyDown”)
$VdsSwitch
| Get-VdsDistributedPortGroup -Name $DVGroup_all |
Set-VdsDVPortTeamingPolicy -ActiveUplink $active_dvuplink1,
$active_dvuplink2
=============
LocalFunctions.ps1
Used for providing Append facility to CSV file
=============
function Export-CSV {
[CmdletBinding(DefaultParameterSetName=’Delimiter’,
SupportsShouldProcess=$true, ConfirmImpact=’Medium’)]
param(
[Parameter(Mandatory=$true, ValueFromPipeline=$true,
ValueFromPipelineByPropertyName=$true)]
[System.Management.Automation.PSObject]
${InputObject},
[Parameter(Mandatory=$true, Position=0)]
[Alias(‘PSPath’)]
[System.String]
${Path},
#region -Append (added by Dmitry Sotnikov)
[Switch]
${Append},
#endregion
[Switch]
${Force},
[Switch]
${NoClobber},
[ValidateSet(‘Unicode’,’UTF7′,’UTF8′,’ASCII’,’UTF32′,’BigEndianUnicode’,’Default’,’OEM’)]
[System.String]
${Encoding},
[Parameter(ParameterSetName=’Delimiter’, Position=1)]
[ValidateNotNull()]
[System.Char]
${Delimiter},
[Parameter(ParameterSetName=’UseCulture’)]
[Switch]
${UseCulture},
[Alias(‘NTI’)]
[Switch]
${NoTypeInformation})
begin
{
# This variable will tell us whether we actually need to append
# to existing file
$AppendMode = $false
try {
$outBuffer = $null
if ($PSBoundParameters.TryGetValue(‘OutBuffer’, [ref]$outBuffer))
{
$PSBoundParameters[‘OutBuffer’] = 1
}
$wrappedCmd = $ExecutionContext.InvokeCommand.GetCommand(‘Export-Csv’,
[System.Management.Automation.CommandTypes]::Cmdlet)
#String variable to become the target command line
$scriptCmdPipeline = ”
# Add new parameter handling
#region Dmitry: Process and remove the Append parameter if it is present
if ($Append) {
$PSBoundParameters.Remove(‘Append’) | Out-Null
if ($Path) {
if (Test-Path $Path) {
# Need to construct new command line
$AppendMode = $true
if ($Encoding.Length -eq 0) {
# ASCII is default encoding for Export-CSV
$Encoding = ‘ASCII’
}
# For Append we use ConvertTo-CSV instead of Export
$scriptCmdPipeline += ‘ConvertTo-Csv -NoTypeInformation ‘
# Inherit other CSV convertion parameters
if ( $UseCulture ) {
$scriptCmdPipeline += ‘ -UseCulture ‘
}
if ( $Delimiter ) {
$scriptCmdPipeline += ” -Delimiter ‘$Delimiter’ ”
}
# Skip the first line (the one with the property names)
$scriptCmdPipeline += ‘ | Foreach-Object {$start=$true}’
$scriptCmdPipeline += ‘{if ($start) {$start=$false} else {$_}} ‘
# Add file output
$scriptCmdPipeline += ” | Out-File -FilePath ‘$Path’ -Encoding ‘$Encoding’ -Append ”
if ($Force) {
$scriptCmdPipeline += ‘ -Force’
}
if ($NoClobber) {
$scriptCmdPipeline += ‘ -NoClobber’
}
}
}
}
$scriptCmd = {& $wrappedCmd @PSBoundParameters }
if ( $AppendMode ) {
# redefine command line
$scriptCmd = $ExecutionContext.InvokeCommand.NewScriptBlock(
$scriptCmdPipeline
)
} else {
# execute Export-CSV as we got it because
# either -Append is missing or file does not exist
$scriptCmd = $ExecutionContext.InvokeCommand.NewScriptBlock(
[string]$scriptCmd
)
}
# standard pipeline initialization
$steppablePipeline = $scriptCmd.GetSteppablePipeline($myInvocation.CommandOrigin)
$steppablePipeline.Begin($PSCmdlet)
} catch {
throw
}
}
process
{
try {
$steppablePipeline.Process($_)
} catch {
throw
}
}
end
{
try {
$steppablePipeline.End()
} catch {
throw
}
}
<#
.ForwardHelpTargetName Export-Csv
.ForwardHelpCategory Cmdlet
Hello,
First of all thank you for the script.
The first script runs with no problems. The second one not so good. Here is what I’m getting if you can help? I’m still learning about scripting so go easy, thanks.
You cannot call a method on a null-valued expression.
At C:UserswillDesktopMigrate-dvSwitch-001Step02-Move-VM-to-vSwitch.ps1:41 char:73
+ NetworkName = switch ($VirtualVmxnet.Backing.GetType <<<< ().Name)
+ CategoryInfo : InvalidOperation: (GetType:String) [], RuntimeException
+ FullyQualifiedErrorId : InvokeMethodOnNull
This is line 41 within the script and char:73 is between "GetType and ()":
NetworkName = switch ($VirtualVmxnet.Backing.GetType().Name)
Send me an e-mail and we’ll work through the script together.
Hello Gabes,
I tried adding the following and still didn’t get anywhere. ($VirtualVmxnet3.Backing.GetType
Subject: [gabesvirtualworld] Re: Migrating distributed vSwitch to new vCenter
Gabe, is this still an obstacle if we want to do a clean install for 5.5? @vCenterGuy recommended doing a clean install at VMworld this year. I don’t see how that’s possible when people are using vDS.
From what I know, yes a dvSwitch is bound to a specific vCenter and cannot be migrated. In 5.1 (I think) there is backup option for the dvSwitch but I don’t know if you can restore to a different vCenter and if the VMs can use those dvPorts again on the new vCenter.
I found out after a bit of troubleshooting that function Get-dvSwitch& function Set-dvSwPgVLAN do not work well if you VDS are organized under a folder. Any clue why that would be an issue and how to modify the function to accept switches that are organized under subfolders.
Be sure to update your VMware Powershell version to the latest. It now has much better dvSwitch commandlets.
I reached out to LucD and found out that he has since updated the function to provide the needed recursive functionality for Get-dvSwitch.
Gabrie – Can you please help? I’m getting the same error as Will. Step 2 & 3 are failing as a result.
Also, can a host be removed from the VDS using PowerCLI?