[CmdletBinding()]
param (
# CSv file tab delimited with name and Vlan
[Parameter(Mandatory = $true)]
[string[]]
$CSV,
# The datacenter where you want to move the workload to.
[Parameter(Mandatory = $true)]
[string]
$Datacenter,
# The Cluster where you want to move the workload to.
[Parameter(Mandatory = $true)]
[string]
$Cluster,
# Errorlog
[Parameter()]
[string]
$ErrorLog = 'E:\Scripts\ErrorLog.txt',
# Enable error logging
[Parameter()]
[switch]
$LogErrors,
# Enable error logging
[Parameter()]
[Int32]
$HostIndex = 0
)
#$Global:ErrorLog = 'E:\Scripts\ErrorLog.txt'
function Write-ToLog {
[cmdletbinding()]
Param (
[string]
[parameter(ValueFromPipeline)]
$Text
)
if ($LogErrors) {
$timestamp = Get-Date
Write-Output "$timestamp :: $Text" | Tee-Object $ErrorLog -Append
}
}
function Move-Workload {
<#
.SYNOPSIS
Performs a migration of VMs running on Hyper-V to vSphere.
.DESCRIPTION
Move-Workload uses Veeam's Instant VM Recovery capability to migrate VMs from Hyper-V to Vsphere.
The script will accept one or multiple Computers/Servers to migrate. It will then lookup the latest restore
point for each Computer and start a Veeam Instant VM Recovery. The restored VM on vSphere will run
directly from the Backup Repository. After the Veeam Instant VM Recovery, a storage vMotion will be
initiated to migrate the VM to production storage and finalize the migration.
.PARAMETER ComputerName
One or more computer names or VM names.
.PARAMETER Datacenter
The Datacenter to where you want to migrate.
.PARAMETER Cluster
The Cluster in the Datacenter to where you want to migrate.
.PARAMETER ErrorLog
When used with -LogErrors, specifies the file path and name to which failed computer names will be written.
.PARAMETER LogErrors
Specify this switch to create a text log file of computers that could not be migrated.
.EXAMPLE
PS C:\> Get-Content "E:\Scripts\Computers.csv" | Move-Workload -Datacenter 'DCR2' -Cluster 'Workloads'
The command will read the list of ComputerNames/hostnames from the CSV file and then migrate each of them
to the Datacenter and Cluster provided.
.EXAMPLE
PS C:\> Move-Workload MAHD-BRU-946,DCRX-LWS-D908 -Datacenter 'DCR2' -Cluster 'Workloads' -LogErrors -Verbose
.INPUTS
Inputs (if any)
.OUTPUTS
Output (if any)
.NOTES
General notes
#>
[CmdletBinding()]
param (
# The name of the computer to migrate and Vlan
[Parameter(Mandatory = $true)]
[string[]]
$ComputerName,
# The Vlan to connect to
[Parameter(Mandatory = $true)]
[string[]]
$vlan,
# The datacenter where you want to move the workload to.
[Parameter(Mandatory = $true)]
[string]
$Datacenter,
# The Cluster where you want to move the workload to.
[Parameter(Mandatory = $true)]
[string]
$Cluster
)
begin {
}
process {
Write-ToLog "Latest available restore point for server $ComputerName was created on $($restorepoint.CreationTime)"
Write-ToLog "Selecting the most suitable ESXi host and Datastore based on available resources"
#$vmhost=(Get-Datacenter -Name $Datacenter | Get-Cluster -Name $Cluster |Get-VMHost -State Connected |Sort-Object $_.MemoryUsageMhz -Descending | Select-Object -First 1)
$vmhost=(Get-Datacenter -Name $Datacenter | Get-Cluster -Name $Cluster |Get-VMHost | Select-Object -Index $HostIndex)
$server = Get-VBRServer -Name $vmhost.name
$datastore = Find-VBRViDatastore -Server $server -Name "*HSA*" |Sort-Object -Property FreeSpace -Descending | Select-Object -First 1
Write-ToLog "Server $ComputerName will be migrated to VMware Host $($server.Name) and stored on Datastore $datastore"
Write-ToLog "Start Veeam Instant VM Recovery of server $ComputerName"
Start-VBRViComputerInstantRecovery -RestorePoint $restorepoint -Server $server -CacheDatastore $datastore -PowerOnAfterRestoring:$false -ConnectVMToNetwork:$true -Reason "VM migration"
Write-ToLog "Veeam Instant VM Recovery for server $ComputerName completed."
#Migrate to production site:
Write-ToLog "Starting migration of server $ComputerName to production Datastore"
$entity = Find-VBRViEntity -Name $ComputerName
Start-VBRQuickMigration -Entity $entity -server $server -datastore $datastore
Write-ToLog "Server $ComputerName has been moved to production Datastore"
#Stop Instant VM Recovery
Write-ToLog "Stopping Veeam Instant VM Recovery"
Get-VBRInstantRecovery |Where-Object{$_.VmName -like $ComputerName} |Stop-VBRInstantRecovery
Write-ToLog "Server $ComputerName has been successfully migrated"
#Change to correct VLAN
$VMwareNetwork = Import-csv ".\vlan.csv" |Where-Object {$_.vlanid -eq $vlan -or $_.system -match $vlan} |Select-Object -Expand Portgroupname
$NIC = Get-NetworkAdapter -VM $ComputerName
Set-NetworkAdapter -NetworkAdapter $NIC -Portgroup $VMwareNetwork -Confirm:$false
Write-ToLog "$ComputerName has been successfully migrated"
#Start-vm $ComputerName
}
end {
}
}
function Set-VMXNET3 {
<#
.SYNOPSIS
Short description
.DESCRIPTION
Long description
.EXAMPLE
PS C:\> <example usage>
Explanation of what the example does
.INPUTS
Inputs (if any)
.OUTPUTS
Output (if any)
.NOTES
General notes
#>
[CmdletBinding()]
param (
# CSv file tab delimited with name and Vlan
[Parameter(Mandatory = $true)]
[string[]]
$ComputerName,
# Parameter help description
[Parameter()]
[System.Management.Automation.PSCredential]
[System.Management.Automation.Credential()]
$GuestCred = [System.Management.Automation.PSCredential]::Empty
)
begin {
}
process {
Write-ToLog "Determining if the Guest OS is Windows or Linux"
$VM = Get-VM -Name $ComputerName
if ($VM.GuestId -like "*Windows*") {
Write-ToLog "The Guest OS is Windows"
Write-ToLog "Starting VM $ComputerName"
Start-VM $ComputerName -Verbose
#Waiting for VMTools to be installed and running
while ($VM.ExtensionData.Guest.ToolsRunningStatus -ne "guestToolsRunning") {
Write-ToLog "Waiting for VMTools installation via SCCM"
Start-Sleep 15
$VM = Get-VM -Name $ComputerName
}
Write-ToLog "VMWare Tools installed and running on $Computername"
try {
#Backup network configuration
Write-ToLog "Taking backup of network configuration"
Invoke-VMScript -VM $ComputerName -ScriptText 'netsh interface dump > "c:\temp\netcfg.dat"' -GuestCredential $GuestCred -Verbose
$ScriptRun = $true
}
catch {
Write-ToLog "Unable to take network configuration backup"
}
if ($ScriptRun) {
try {
Write-ToLog "Checking for network configuration backup file"
Invoke-VMScript -VM $ComputerName -ScriptText 'dir "c:\temp\netcfg.dat"' -GuestCredential $GuestCred -Verbose
$ScriptRun = $true
}
catch {
Write-ToLog "Network configuration backup file not present"
}
}
if ($ScriptRun) {
Write-ToLog "Network configuration backup created"
Write-ToLog "Shutting down VM $ComputerName"
$VM | Shutdown-VMGuest -Confirm:$false
}
while ($VM.PowerState -ne "PoweredOff") {
Write-ToLog "Waiting for $ComputerName to shutdown"
Start-Sleep 5
$VM = Get-VM -Name $ComputerName
}
Write-ToLog "$Computername has been shutdown"
#Change E1000 to VMXNET3
Write-ToLog "Change NIC from E1000 to VMXNET3"
Get-VM -Name $ComputerName |Get-NetworkAdapter |Set-NetworkAdapter -Type Vmxnet3 -Confirm:$false -Verbose
Write-ToLog "Starting VM $ComputerName"
Start-VM -VM $ComputerName
while ($VM.ExtensionData.Guest.ToolsRunningStatus -ne "guestToolsRunning") {
Write-ToLog "Waiting for $ComputerName to Power On"
Start-Sleep 5
$VM = Get-VM -Name $ComputerName
}
#Restore network configuration on new NICs
Write-ToLog "Restore network configuration to the new VMXNET3 network adapter"
Invoke-VMScript -VM $ComputerName -ScriptText 'netsh exec "c:\temp\netcfg.dat"' -Verbose
}
else{
Write-ToLog "The Guest OS is Linux"
Write-ToLog "Setting VM Guest OS to CentOS7 "
get-vm -name $ComputerName |set-vm -guestid "centos7_64Guest" -Confirm:$false
#Change E1000 to VMXNET3
Write-ToLog "Changing NIC from E1000 to VMXNET3"
Get-VM -Name $ComputerName |Get-NetworkAdapter |Set-NetworkAdapter -Type Vmxnet3 -Confirm:$false
#Move VM to LNX folder
Write-ToLog "Moving VM to LNX folder"
$VMFolder = Get-Folder -Type VM -Name "LNX"
Get-VM -Name $ComputerName | Move-VM -Destination $VMFolder
Write-ToLog "Starting VM $ComputerName"
Start-VM $ComputerName
}
}
end {
}
}
function Set-VMHardware {
[CmdletBinding()]
param (
#
[Parameter(Mandatory = $true)]
[string]
$ComputerName
)
begin {
}
process {
Write-ToLog "Change VM $ComputerName Hardware version"
Set-VM $ComputerName -HardwareVersion vmx-19 -Confirm:$false -Verbose
}
end {
}
}
function Remove-FromHyperVBackup {
[CmdletBinding()]
param (
#
[Parameter(Mandatory = $true)]
[string]
$ComputerName
)
begin {
}
process {
#Exclude VM from Hyper-V Backup job
Get-VBRJob |Where-Object{$_.SourceType -eq "HyperV"} | Get-VBRJobObject -Name $ComputerName | Remove-VBRJobObject -Verbose
Write-ToLog "VM $ComputerName has been migrated and excluded from the Veeam Hyper-V Backup Job"
}
end {
}
}
function Start-Migration {
[CmdletBinding()]
param (
)
begin {
$StartTime = Get-Date
Write-ToLog "`n`n"
Write-ToLog "Start Migration at $StartTime"
Write-ToLog "Log name is $ErrorLog"
Write-ToLog "CSV file with list of computers used to migrate is $CSV"
Write-ToLog ("=" * 80)
#Connect to local Veeam Backup Server
Connect-VBRServer
#Connect to vSphere
$credVC = Get-Credential -Message "Provide credentials to connect to vCenter"
Set-PowerCLIConfiguration -InvalidCertificateAction Ignore -Confirm:$false -Scope User -ParticipateInCeip $false
Connect-VIServer -Server vcenter.labo.be -credential $credVC
#Store Windows Guest Credentials
$GuestCred = Get-Credential -Message "Provide Windows Guest Credentials or cancel if Linux"
}
process {
$computers = Import-csv -delimiter "`t" $csv
foreach ($Computer in $Computers) {
$ComputerName = $computer.name
$vlan = $computer.vlan
Write-ToLog "Querying computer $ComputerName for restorepoints"
$VBRBackup = Get-VBRBackup |Where-Object {$_.JobSourceType -eq "HyperV"}
$restorepoint = $VBRBackup | Get-VBRRestorePoint -Name $ComputerName | Sort-Object creationtime -Descending | Select-Object -First 1
if ($null -eq $restorepoint) {
Write-Warning "Could not start migration for server $ComputerName. No restore points found."
if ($LogErrors) {
Write-ToLog "WARNING :: $ComputerName No restore points found"
}
}
else {
Move-Workload -ComputerName $ComputerName -vlan $vlan -Datacenter $Datacenter -Cluster $Cluster -Verbose
Set-VMHardware -ComputerName $ComputerName -Verbose
Set-VMXNET3 -ComputerName $ComputerName -GuestCred $GuestCred -Verbose
Remove-FromHyperVBackup -ComputerName $ComputerName -Verbose
}
}
}
end {
#Disconnect from local Veeam Backup Server
Disconnect-VBRServer
#Disconnect from vCenter
Disconnect-VIServer -Confirm:$false
}
}
$data
Start-Migration
Hi guys
I wanted to share a powershell script we made for a customer to migrate VMs from Hyper-V to vSphere. It’s not perfect but it gets the job done.
There are still some issues with the transformation of E1000 to VMXNET3. The Linux team managed to get this working by using puppet. I’m still working on the Windows part.
As the Veeam environment is not domain joined, we cannot use remote powershell.
VMware Tools on Windows machines are pushed by SCCM.