VMware to Hyper-V Migration with MAT and my addons

Today I was on Sommarkollo at Microsoft Sweden and speaking on the event and this was the last of two sessions about the MAT (Migration Automation Toolkit).

This automation tool that I first got to know about at MMS 2013 in Las Vegas (sadly it looks like there will be no more MMS 🙁 but that is another story ) in the session held by “Migration Mark” and Matt McSpirit WS-B325 and there in the video about 41 minutes into I ask about how the MAT take care of IP addresses on the migrated VM´s and they answered me that is not part of the MAT yet! And well I would say that this would be quite painful to migrate 100-200 VM´s with this automation tool and still have to enter IP settings for each migrated vm manually!!

But I have an example on how that can be done, I have not integrated it into the MAT scripts yet but it automates the collection of not just the IP addresses but also dns,subnet and gateway.

The information is available via the PowerCLI from the vCenter and here I show you how you can collect all that info after you have got the migration list

# Collect Networking info from VM´s 
#
# Niklas Akerlund / 2013-07-01
if(!(Get-PSSnapin "VMware.VimAutomation.Core" -ErrorAction SilentlyContinue)){
    Add-PSSnapin "VMware.VimAutomation.Core"
}
$MigVMs = Get-Content "d:\mat\vmlist.txt"
$VMNetConf = @()
Connect-VIServer vCenter.vniklas.com
remove-module Hyper-V -ErrorAction SilentlyContinue

foreach ($MigVM in $MigVMs){
     $VM = Get-VM $MigVM.Split(".")[0]
     #write-host $VM
     #$VM = Get-VM $VM 
     $VMNics = Get-VMGuestNetworkInterface $VM | where {$_.IP -ne $null -and $_.IPPolicy -eq "Static"}
     foreach ($VMNic in $VMNics){
         $VMNetAdapter = Get-NetworkAdapter -VM $VMNic.VM -Name $VMNic.NetworkAdapter
          $obj = [PSCustomObject]@{
                        VMName = $VM.Name
                        NetworkName = $VMNetAdapter.NetworkName
                        IP = $VMNic.Ip
                        Subnet = $VMNic.SubnetMask
                        DNS = [string]$VMNic.Dns
                        GW = $VMNic.DefaultGateway
                        }
          $VMNetConf +=$obj
     }
}

$VMNetConf | Export-csv -Path d:\mat\vmniclist.txt -NoTypeInformation -NoClobber -UseCulture

Here you can see the csv file after it has run in my test environment, one thing that I will extend with after discussing with some folks is the vlan number the port group the vm is connected to so that can be configured on the other side also.

Screen Shot 2013-08-15 at 23.30.25

And then I also with the possibility that there is in Hyper-V 2012, configure the network on the guest from the host, this requires that the virtual machine has the latest integration components installed. Ravikanth has done a blog post about how his function, I have added the possibility to run it from a remote computer

# Set-VMNetworkConfiguration
# Orginial by Ravikanth http://www.ravichaganti.com/blog/?p=2766
#
# Added computername to remotly set IP to guest from hosts
# Niklas Akerlund
Function Set-VMNetworkConfiguration {
    [CmdletBinding()]
    Param (
        
        [Parameter(Mandatory=$true,
                   Position=1,
                   ParameterSetName='DHCP',
                   ValueFromPipeline=$true)]
        [Parameter(Mandatory=$true,
                   Position=0,
                   ParameterSetName='Static',
                   ValueFromPipeline=$true)]
        [Microsoft.HyperV.PowerShell.VMNetworkAdapter]$NetworkAdapter,
        
        [Parameter(Mandatory=$true,
                   Position=2,
                   ParameterSetName='DHCP',
                   ValueFromPipeline=$true)]
        [Parameter(Mandatory=$true,
                   Position=5,
                   ParameterSetName='Static',
                   ValueFromPipelineByPropertyName=$true)]
        [String[]]$ComputerName=@(),

        [Parameter(Mandatory=$true,
                   Position=1,
                   ParameterSetName='Static')]
        [String[]]$IPAddress=@(),

        [Parameter(Mandatory=$false,
                   Position=2,
                   ParameterSetName='Static')]
        [String[]]$Subnet=@(),

        [Parameter(Mandatory=$false,
                   Position=3,
                   ParameterSetName='Static')]
        [String[]]$DefaultGateway = @(),

        [Parameter(Mandatory=$false,
                   Position=4,
                   ParameterSetName='Static')]
        [String[]]$DNSServer = @(),
         
        [Parameter(Mandatory=$true,
                   Position=0,
                   ParameterSetName='DHCP')]
        [Switch]$Dhcp
        
    )

    $VM = Get-WmiObject -Namespace 'root\virtualization\v2' -Class 'Msvm_ComputerSystem' -ComputerName $ComputerName | Where-Object { $_.ElementName -eq $NetworkAdapter.VMName } 
    $VMSettings = $vm.GetRelated('Msvm_VirtualSystemSettingData') | Where-Object { $_.VirtualSystemType -eq 'Microsoft:Hyper-V:System:Realized' }    
    $VMNetAdapters = $VMSettings.GetRelated('Msvm_SyntheticEthernetPortSettingData') 

    $NetworkSettings = @()
    foreach ($NetAdapter in $VMNetAdapters) {
        if ($NetAdapter.Address -eq $NetworkAdapter.MacAddress) {
            $NetworkSettings = $NetworkSettings + $NetAdapter.GetRelated("Msvm_GuestNetworkAdapterConfiguration")
        }
    }

    if ($Dhcp) {
        $NetworkSettings[0].DHCPEnabled = $true
    } else {
        $NetworkSettings[0].DHCPEnabled = $false
        $NetworkSettings[0].IPAddresses = $IPAddress
        $NetworkSettings[0].Subnets = $Subnet
        $NetworkSettings[0].DefaultGateways = $DefaultGateway
        $NetworkSettings[0].DNSServers = $DNSServer
        $NetworkSettings[0].ProtocolIFType = 4096
    }

    $Service = Get-WmiObject -Class "Msvm_VirtualSystemManagementService" -Namespace "root\virtualization\v2" -ComputerName $ComputerName
    $setIP = $Service.SetGuestNetworkAdapterConfiguration($VM, $NetworkSettings[0].GetText(1))

    if ($setip.ReturnValue -eq 4096) {
        $job=[WMI]$setip.job 

        while ($job.JobState -eq 3 -or $job.JobState -eq 4) {
            start-sleep 1
            $job=[WMI]$setip.job
        }

        if ($job.JobState -eq 7) {
            write-host "Success"
        }
        else {
            $job.GetError()
        }
    } elseif($setip.ReturnValue -eq 0) {
        Write-Host "Success"
    }
}

And here is the script that I run to configure the VM´s after the MAT has done the conversions, and yes as Mark says about MAT, there is room for improvements here also of course, As you can see I start with injecting the latest integration components into the VM, the sleep cmdlet is used to get the vm fully booted and integration components installed and then an reboot inside the VM.

# Configure and set VM after Conversion
#
# Niklas AKerlund / 2013-07-01

# Function import
. .\Set-VMNetworkConfiguration.ps1

$VMNICs = Import-CSV -Path D:\mat\vmniclist.txt -Delimiter ";"
$ConvertedVMs = Get-Content -Path D:\mat\VMlist.txt

# Configure each vm before starting
foreach ($ConvertedVM in $ConvertedVMs){
    $VM = Get-VM $ConvertedVM.Split(".")[0]
    write-host $VM.Name
    #patch each VM with latest Integration Tools
    $virtualHardDiskToUpdate =($VM | Get-VMHardDiskDrive).path
    $integrationServicesCabPath ="C:\Windows\vmguest\support\amd64\Windows6.x-HyperVIntegrationServices-x64.cab"

    #Mount the VHD
    $diskNo=(Mount-VHD -Path $virtualHardDiskToUpdate –Passthru).DiskNumber

    #Get the driver letter associated with the mounted VHD, note this assumes it only has one partition if there are more use the one with OS bits
    $driveLetter=(Get-Disk $diskNo | Get-Partition | where Size -GT 100MB).DriveLetter

    #Check to see if the disk is online if it is not online it
    if ((Get-Disk $diskNo).OperationalStatus -ne 'Online'){Set-Disk $MountedVHD.Number -IsOffline:$false -IsReadOnly:$false}

    #Install the patch
    Add-WindowsPackage -PackagePath $integrationServicesCabPath -Path ($driveLetter + ":\")

    #Dismount the VHD
    Dismount-VHD -Path $virtualHardDiskToUpdate
    
    Start-VM -VM $VM

    Start-Sleep -Seconds 300
    
    # Wait for the Integration components being installed and the server reboot (reboot requires interaction or automatic script inside VM or you will have to do an unclean poweroff

    Start-VM -VM $VM

    # check that the migrated VM actually has ic that responds 
    do {
        $notok = Get-VMIntegrationService -VM $VM | Select -First 1 | where PrimaryStatusDescription -eq "OK"
    } while( $notok -eq $null)

    # COnfigure NICs
    foreach ($VMNic in $VMNICs){
        write-host "Configuring" 
        if($VMNIC.VMName -eq $VM.Name){
           if(!(Get-VMNetworkAdapter -VM $VM)){
               $VMNetAdapter = Add-VMNetworkAdapter -VM $VM -SwitchName $VMNic.NetworkName -Passthru
               $VMNetAdapter | Set-VMNetworkConfiguration -IPAddress $VMNic.IP -DefaultGateway $VMNic.GW -Subnet $VMNic.SubnetMask -DNSServer $VMNic.Dns -ComputerName HV03
         
           }else {
           
               Connect-VMNetworkAdapter -VMName $VM.Name -SwitchName $VMNic.NetworkName
               $VMNetAdapter = Get-VMNetworkAdapter -VM $VM
               write-host "connecting $VMNic.NetworkName "
               $VMNetAdapter | Set-VMNetworkConfiguration -IPAddress $VMNic.IP -DefaultGateway $VMNic.GW -Subnet $VMNic.Subnet -DNSServer $VMNic.DNS -ComputerName HV03
          }
        }
    }

}

And after running as you can see in the Hyper-V manager I have the right IP on the VM (and if you check inside the VM, you will see that the dns,subnet and gateway also has been set correctly.

Screen Shot 2013-08-15 at 23.38.27

Comments

tasos
Reply

Hello.I’m trying to use Set-VMNetworkConfiguration on a linux vm but i get an wmi error.The integration services are installed.Any ideas?Have you made it work on linux?
Thanks in advanced!

Francesco
Reply

Hi Niklas,

can you say me how i can set the alternate DNS Server with “Set-VMNetworkConfiguration” Script?

Thx for your Feedback.

Leave a comment

name*

email* (not published)

website