<#
.SYNOPSIS
Perform steps to migrate all ESXi hosts in a cluster to another vCenter Server.
.DESCRIPTION
Use this script to migrate all ESXi hosts in a cluster to another vCenter Server.
After the host is migrated the running VMs are moved to the folder they were in on the source vCenter Server.
The folders must be available before this script is used.
This script assumes availability of the following modules:
- VMware-PowerCli
.PARAMETER
- Cluster: name of the cluster to migrate.
.EXAMPLE
.\Migrate-Hosts.ps1 -Cluster <clustername>
.NOTES
Script name: Migrate-Hosts.ps1
Author: Jeroen Buren
DateCreated: 03-03-2020
#>
[CmdletBinding(SupportsShouldProcess=$true)]
param(
[parameter(Mandatory=$true,HelpMessage="Which cluster do you want to migrate?")][string]$Cluster,
[parameter(Mandatory=$true,HelpMessage="What is the root folder for VMs?")][string]$RootFolder
)
####################################################################################
# #
# Functions #
# #
####################################################################################
function Get-ScriptDirectory {
$Invocation = (Get-Variable MyInvocation -Scope 1).Value
Split-Path $Invocation.MyCommand.Path
}
function Log {
param(
[Parameter(Mandatory=$true)][String]$Message,
[Parameter(Mandatory=$true)][ValidateSet("Info","Debug","Warn","Error")][String]$Type,
[Parameter(Mandatory=$true)][ValidateSet("Console","LogFile","Both")][String]$OutputMode
)
$dateTimeString = Get-Date -Format "yyyy-MM-dd HH:mm:sszz"
$output = ($dateTimeString + " " + $type.ToUpper() + " " + $message)
if ($outputMode -eq "Console" -OR $outputMode -eq "Both") {
Write-Host $output
}
if ($outputMode -eq "LogFile" -OR $outputMode -eq "Both") {
try {
Add-Content $logFile -Value $output -ErrorAction Stop
}
catch {
Log ("Failed to write to log file: """ + $logFile + """.") -OutputMode Console -Type Error
Log ("[" + $_.Exception.GetType().FullName + "] " + $_.Exception.Message) -OutputMode Console -Type Error
}
}
}
filter Get-FolderPath {
$_ | Get-View | % {
$row = "" | select Name, Path
$row.Name = $_.Name
$current = Get-View -Server $SourcevCenter $_.Parent
$path = $_.Name
do {
$parent = $current
if($parent.Name -ne "vm"){$path = $parent.Name + "\" + $path}
$current = Get-View -Server $SourcevCenter $current.Parent
} while ($current.Parent -ne $null)
$row.Path = $path
$row
}
}
####################################################################################
# #
# Variables #
# #
####################################################################################
# Logfile
Set-Variable logFile ((Get-ScriptDirectory) + "\Migrate-Hosts.log") -Option Constant -ErrorAction SilentlyContinue
$SourcevCenter = "<source vcenter>"
$DestinationvCenter = "<destination vcenter>"
####################################################################################
# #
# Main Code #
# #
####################################################################################
Log -Message "Script started" -Type Info -OutputMode LogFile
$cred = Get-Credential -Message "Enter valid ESXi host credentials"
# Connect source vCenter Server
Connect-VIserver -Server $SourcevCenter | Out-Null
Log -Message "Connected to source vCenter Server" -Type Info -OutputMode Both
# Connect destination vCenter Server
Connect-VIServer -Server $DestinationvCenter | Out-Null
Log -Message "Connected to destination vCenter Server" -Type Info -OutputMode Both
# Get DRS Host rules from cluster
$DRSVmHostRules = Get-DrsVMHostRule -Cluster $Cluster -Server $SourcevCenter
# Get DRS VM rules from cluster
$rules = @()
foreach ($rule in Get-DrsRule -Cluster $Cluster -Server $SourcevCenter) {
$line = ($rule.Name + "," + $rule.Enabled + "," + $rule.Type)
foreach ($vmId in $rule.VMIds) {
$line += ("," + (Get-View -Id $vmId).Name)
}
$rules += $line
$rules | Out-File D:\Scripts\VMware\Temp\$Cluster-DRSRules.txt
}
# Get DRS VM groups from cluster
Get-DrsClusterGroup -Cluster $Cluster -Type VMGroup -Name "VM DRS Group 1" -Server $SourcevCenter | Select -ExpandProperty Member | Select Name | Export-Csv "D:\Scripts\VMware\Temp\$Cluster-1.csv" -NoTypeInformation
Get-DrsClusterGroup -Cluster $Cluster -Type VMGroup -Name "VM DRS Group 2" -Server $SourcevCenter | Select -ExpandProperty Member | Select Name | Export-Csv "D:\Scripts\VMware\Temp\$Cluster-2.csv" -NoTypeInformation
# Start migration
foreach ($esxi in Get-Cluster -Name $Cluster -Server $SourcevCenter | Get-VMHost) {
Log -Message "Migrating host $($esxi.Name)" -Type Info -OutputMode Both
# Get folderpath from VMs on host
$report = @()
$report = Get-VMHost $esxi -Server $SourcevCenter | Get-VM | Get-Folderpath
$report | Export-Csv "D:\Scripts\VMware\Temp\$($esxi)-VMs-With-FolderPath.csv" -NoTypeInformation
Log -Message "Exported folder paths from VMs" -Type Info -OutputMode Both
# Disconnect host from source vCenter
Set-VMHost -Server $SourcevCenter -VMHost $esxi -State Disconnected -Confirm:$false
Log -Message "Disconnected host $($esxi.Name) from source vCenter Server" -Type Info -OutputMode Both
# Remove host from source vCenter
Remove-VMHost -Server $SourcevCenter -VMHost $esxi -Confirm:$false
Log -Message "Removed host $($esxi.Name) from source vCenter Server" -Type Info -OutputMode Both
# Add host to destination vCenter
Try {Add-VMHost -Server $DestinationvCenter -Name $esxi.Name -Credential $cred -Location "Datacenter" -Force}
Catch {Log -Message "Oops... Something went wrong..." -Type Error -OutputMode Both}
Log -Message "Added host $($esxi.Name) to destination vCenter Server" -Type Info -OutputMode Both
# Move host to cluster
Try {Move-VMHost -Server $DestinationvCenter -VMHost $(Get-VMHost -Name $esxi.Name -Server $DestinationvCenter) -Destination $Cluster}
Catch {Log -Message "Oops... Something went wrong..." -Type Error -OutputMode Both}
Log -Message "Moved host $($esxi.Name) to cluster $Cluster" -Type Info -OutputMode Both
# Move VMs to folder and start with next host
$VMfolder = @()
$VMfolder = Import-Csv "D:\Scripts\VMware\Temp\$($esxi)-VMs-With-FolderPath.csv" | Sort-Object -Property Path
foreach ($guest in $VMfolder) {
$key = @()
$key = Split-Path $guest.Path | split-path -leaf
if ($key -eq "Datacenter") {
Log -Message "Moving $($guest.Name) to folder $RootFolder" -Type Info -OutputMode Both
Move-VM (Get-VM $guest.Name -Server $DestinationvCenter) -InventoryLocation (Get-Folder -Name $RootFolder -Type VM -Server $DestinationvCenter) | Out-Null
}
else {
Log -Message "Moving $($guest.Name) to folder $key" -Type Info -OutputMode Both
Move-VM (Get-VM $guest.Name -Server $DestinationvCenter) -InventoryLocation (Get-Folder $key -Server $DestinationvCenter -Location $RootFolder) -ErrorAction SilentlyContinue | Out-Null
}
}
}
# Create DRS Cluster Host Groups
$Hosts1 = Get-Cluster -Name $Cluster -Server $DestinationvCenter | Get-VMHost | where {$_.Name.Substring(4,1) -eq "0"}
$Hosts2 = Get-Cluster -Name $Cluster -Server $DestinationvCenter | Get-VMHost | where {$_.Name.Substring(4,1) -eq "1"}
Log -Message "Creating DRS Host Groups" -Type Info -OutputMode Both
New-DrsClusterGroup -Name "Host DRS Group 1" -Cluster $Cluster -VMHost $Hosts1 | Out-Null
New-DrsClusterGroup -Name "Host DRS Group 2" -Cluster $Cluster -VMHost $Hosts2 | Out-Null
# Create DRS Cluster VM Groups
$VMsAMS = @()
$VMsWSN = @()
foreach ($vm in Import-Csv "D:\Scripts\VMware\Temp\$Cluster-1.csv") {$VMs1 += Get-VM -Name $vm.Name -Server $DestinationvCenter}
foreach ($vm in Import-Csv "D:\Scripts\VMware\Temp\$Cluster-2.csv") {$VMs2 += Get-VM -Name $vm.Name -Server $DestinationvCenter}
Log -Message "Creating DRS VM Groups" -Type Info -OutputMode Both
New-DrsClusterGroup -Name "VM DRS Group 1" -Cluster $Cluster -Server $DestinationvCenter -VM $VMs1 | Out-Null
New-DrsClusterGroup -Name "VM DRS Group 2" -Cluster $Cluster -Server $DestinationvCenter -VM $VMs2 | Out-Null
# Enable DRS on the cluster
Set-Cluster -Cluster $Cluster -Server $DestinationvCenter -DrsEnabled $true -Confirm:$false | Out-Null
# Create DRS Host rules
foreach ($rule in $DRSVmHostRules) {
$VMGroup = Get-DrsClusterGroup -Name $rule.VMGroup -Cluster $Cluster -Type VMGroup -Server $DestinationvCenter
$VMHostGroup = Get-DrsClusterGroup -Name $rule.VMHostGroup -Cluster $Cluster -Type VMHostGroup -Server $DestinationvCenter
New-DrsVMHostRule -Name $rule.Name -Enabled $true -Cluster $Cluster -VMGroup $VMGroup -VMHostGroup $VMHostGroup -Type ShouldRunOn -Server $DestinationvCenter | Out-Null
}
# Create DRS VM rules
foreach ($rule in $rules) {
$rulearr = $rule.Split(",")
if ($rulearr[1] -eq "True") {$enabled = $true}
if ($rulearr[2] -eq "VMAntiAffinity") {$together = $false}
New-DrsRule -Server $DestinationvCenter -Name $rulearr[0] -Cluster $Cluster -Enabled $enabled -KeepTogether $together -VM (Get-VM -Name ($rulearr[3..($rulearr.Count -1)]) -Server $DestinationvCenter)
}
# Disconnect from vCenter Servers
Disconnect-VIServer -Server $SourcevCenter -Confirm:$false
Disconnect-VIServer -Server $DestinationvCenter -Confirm:$false
Log -Message "Script Ended" -Type Info -OutputMode LogFile