Last year i attend the Dutch VMUG (NLVMUG) i followed session from
Michael Wilmsen that was: Migrate your datacenter without downtime.
I must also move al lot of VM’s from different datacenters to other datacenters.
I use the script from Michael Wilmsen to move the VM’s. But along the way I counter some problems with this script. So I begon tweaking and tweaking and tweaking this script to create for me the ultimate Cross vCenter PowerCLI Script.
Coolfeatures:
– Info through Whattsapp (Default not enabled)
– Dryrun (Test Run)
– Logging
– Selection through GUI
– Multiple Nic support maximum of 4.
– Datastore en Host selection based on Free space en Free Memory
– Check of Destination Host or Datastore in Maintance
– Destination Store exist in Destination Cluster
MoveVM.ps1:
#Filename: MoveVM.ps1
#Author: M. Wilmsen / W. Vissers
#Source: http://virtual-hike.com/nlvmug-2018/
#Version: 2.0
#Date: 21-10-2018
#ChangeLog:
# V0.9 – M. Wilmsen First Version
# V1.0 – Fixed Multiple Nics to maximium of 4 nics
# – Logfile name VM name
# V1.1 – Destination Cluster not the first Host
# V1.2 – Selected Destination host based on memory used
# V1.3 – Fixed folder location and VirtualPortGroup
# V1.4 – Fixed Datastore in Maintance
# V1.5 – Using Get-VICredentialStoreItem + Logpath Fixt
# V1.6 – Fixed Log in Hours in 24 uurs
# V1.7 – Fixed Using DatastoreCluster name based on Cluster name!
# V1.8 – Check if Destination has the same datastore
# – Ask know for input
# – VM selection with VMhost
# – Fixed Ping Check
# v1.9 – Added Destination Store exist in Destination Cluster
# v2.0 – Fixed Destination Store exist in Destination Cluster
<#
.SYNOPSIS
Script to migrate a virtual machine
.DESCRIPTION
Script to migrate compute and storage from cluster to cluster. Log will be in current dir [VM]-[-timestamp].log
.EXAMPLE
MoveVM.ps1
#>
################################## INIT #################################################
#Set WebOperation timeout
# set-PowerCLIConfiguration -WebOperationTimeoutSeconds 3600
#Define Global variables
$location = “D:\xmovewhattsapp”
$LogPath = “.\”
$DataStoreClusterPrefix = “SAN-“
$SourceVC = Read-Host “Give Source vCenter”
$DestinationVC = Read-Host “Give Destination vCenter”
$DRSRecommendation = $true
$Dryrun = $false
$SendWhatsApp = $false
$WhatsAppNumbers = “0123456789”
$WhatsAppGroup = “Namehireyourwhattsgroup”
$instanceId = “23” #chang this line
$clientId = “demo@demo.nl” #change this line
$clientSecret = “Puthiersecretid” #change this line
################################## PASSWORD STORE ##############################################
#Username
# Check if credentials exist in credential store if not ask for credentials and put them in credential store
If ((Get-VICredentialStoreItem).host -notcontains $SourceVC) {New-VICredentialStoreItem -Host $SourceVC -User $env:USERNAME -Password ((get-credential).GetNetworkCredential().Password)}
If ((Get-VICredentialStoreItem).host -notcontains $DestinationVC) {New-VICredentialStoreItem -Host $DestinationVC -User $env:USERNAME -Password ((get-credential).GetNetworkCredential().Password)}
# Remove-VICredentialStoreItem * -Confirm:$false
################################## END INIT #################################################
################################## FUNCTIONS #################################################
#Define log function
Function LogWrite
{
Param ([string]$logstring)
#Add logtime to entry
$LogTime = Get-Date -Format “MM-dd-yyyy_HH-mm-ss”
$logstring = $LogTime + ” : ” + $logstring
#Write logstring
Add-content $LogFile -value $logstring
Write-Host $logstring
}
#Define SendWhatsApp function
Function SendWhatsApp
{
Param ([string] $message)
if ( $SendWhatsApp ) {
$LogTime = Get-Date -Format “MM-dd-yyyy_hh-mm-ss”
$message = $logtime + ” : ” + $message
foreach ( $number in $WhatsAppNumbers )
{
$jsonObj = @{‘group_admin’=$number;
‘group_name’=$WhatsAppGroup;
‘message’=$message;}
Try {
$res = Invoke-WebRequest -Uri “http://api.whatsmate.net/v2/whatsapp/group/message/$instanceId” `
-Method Post `
-Headers @{“X-WM-CLIENT-ID”=$clientId; “X-WM-CLIENT-SECRET”=$clientSecret;} `
-Body (ConvertTo-Json $jsonObj)
LogWrite “WhatsMate Status Code: ” $res.StatusCode
LogWrite $res.Content
}
Catch {
$result = $_.Exception.Response.GetResponseStream()
$reader = New-Object System.IO.StreamReader($result)
$reader.BaseStream.Position = 0
$reader.DiscardBufferedData()
$responseBody = $reader.ReadToEnd();
Write-host “Status Code: ” $_.Exception.Response.StatusCode
Write-host $message
}
}
}
}
function Get-VmSize($vm)
{
#Initialize variables
$VmDirs =@()
$VmSize = 0
$searchSpec = New-Object VMware.Vim.HostDatastoreBrowserSearchSpec
$searchSpec.details = New-Object VMware.Vim.FileQueryFlags
$searchSpec.details.fileSize = $TRUE
Get-View -VIObject $vm | % {
#Create an array with the vm’s directories
$VmDirs += $_.Config.Files.VmPathName.split(“/”)[0]
$VmDirs += $_.Config.Files.SnapshotDirectory.split(“/”)[0]
$VmDirs += $_.Config.Files.SuspendDirectory.split(“/”)[0]
$VmDirs += $_.Config.Files.LogDirectory.split(“/”)[0]
#Add directories of the vm’s virtual disk files
foreach ($disk in $_.Layout.Disk) {
foreach ($diskfile in $disk.diskfile){
$VmDirs += $diskfile.split(“/”)[0]
}
}
#Only take unique array items
$VmDirs = $VmDirs | Sort | Get-Unique
foreach ($dir in $VmDirs){
$ds = Get-Datastore ($dir.split(“[“)[1]).split(“]”)[0]
$dsb = Get-View (($ds | get-view).Browser)
$taskMoRef = $dsb.SearchDatastoreSubFolders_Task($dir,$searchSpec)
$task = Get-View $taskMoRef
while($task.Info.State -eq “running” -or $task.Info.State -eq “queued”){$task = Get-View $taskMoRef }
foreach ($result in $task.Info.Result){
foreach ($file in $result.File){
$VmSize += $file.FileSize
}
}
}
}
return $VmSize
}
################################## END FUNCTIONS #################################################
#Login to vCenter servers
if (($global:DefaultVIServers).Name -notcontains $SourceVC -or $DestinationVC) {
#SourceVC
$ConnectVC = Connect-VIServer $SourceVC
$Message = “Connecting to ” + $ConnectVC + ” as ” + $env:USERNAME
#Logwrite $Message
#DestionationVC
$ConnectVC = Connect-VIServer $DestinationVC
$Message = “Connecting ” + $ConnectVC + ” as ” + $env:USERNAME
#Logwrite $Message
# Disconnect-VIServer * -Confirm:$false
}
Set-Location $location
$cluster=Get-Cluster -Server $SourceVC | Out-GridView -OutputMode Single -Title “Select Source Cluster”
$vmtomigrate =Get-Cluster $cluster -Server $SourceVC | Get-VM | Out-GridView -OutputMode Single -Title “Select VM”
$DestinationCluster = Get-Cluster -Server $DestinationVC | Out-GridView -OutputMode Single -Title “Select Destination Cluster”
$vmfolder=Get-folder -Server $DestinationVC | Out-GridView -OutputMode Single -Title “Select Folder”
#Main Script
#Set $MigError to false befor migration
$MigError = $false
#Get VM variables
$vm = get-vm $vmtomigrate
#Define LogFile with time stamp
$LogTime = Get-Date -Format “MM-dd-yyyy_hh-mm-ss”
if([IO.Directory]::Exists($LogPath))
{
#Do Nothing!!
}
else
{
New-Item -ItemType directory -Path $LogPath
}
$LogFile = $LogPath+$VM+”-“+$LogTime+”.log”
# LogWrite Gebruiker
Logwrite $env:USERNAME
# Get-VM Info
$VMHDDSize = Get-VmSize($vm)
$VMHDDSize = [Math]::Round(($VMHDDSize / 1GB),2)
Logwrite “Start Virtual Machine Move”
#If WhatsApp make notice
if ( $SendWhatsApp ) { LogWrite “Notifications will be send using WhatsApp to WhatsApp Group: $WhatsAppGroup” }
#If DryRun make Notice
if ( $Dryrun ) {
Logwrite “Start move virtual machines $vm Disksize $VMHDDSize GB (DryRun)”
SendWhatsApp “Start move virtual machines $vm Disksize $VMHDDSize GB(DryRun)”
}
else {
Logwrite “Start move virtual machines $vm Disksize $VMHDDSize GB”
SendWhatsApp “Start move virtual machines $vm Disksize $VMHDDSize GB”
}
$SourceCluster = get-vm $vm | Get-Cluster | select name
$vmip = $vm | Select @{N=”IP Address”;E={@($_.guest.IPAddress[0])}}
$vmip = $vmip.”ip address”
$VMHDDSize = Get-VmSize($vm)
$VMHDDSize = [Math]::Round(($VMHDDSize / 1GB),2)
$NetworkAdapter = Get-NetworkAdapter -VM $vm -Server $SourceVC
$SourceVMPortGroup = Get-NetworkAdapter -vm $vm | Select NetworkName
$switchname = $DestinationCluster
$Datastore = Get-VM $vm | Get-DataStore -Server $sourceVC | Select @{N=”Name”;E={@($_.Name)}}
$Datastore = $Datastore.Name
$DatastoreExistinOthervCenter = Get-Cluster $DestinationCluster | Get-DataStore -Server $DestinationVC | ? {$_.Name -like “*$Datastore*”}
if ($DatastoreExistinOthervCenter )
{
LogWrite “Datastore exsist $DestinationCluster in destination vCenter $DestinationVC “
$destinationDatastore = $DatastoreExistinOthervCenter }
Else
{
LogWrite “Datastore does not exsist in $DestinationCluster destination vCenter $DestinationVC”
# Select DataStore with the most free space and not in maintance
$DatastoreCluster = “$DataStoreClusterPrefix”+”$DestinationCluster”
$destinationDatastore = Get-DatastoreCluster $DatastoreCluster | Get-Datastore | Where {$_.State -ne “Maintenance”} | Sort-Object -Property FreeSpaceGB -Descending | Select-Object -First 1
}
$destinationDatastoreFreeSpace = $destinationDatastore | Select Name,@{N=”FreeSpace”;E={$_.ExtensionData.Summary.FreeSpace}}
$destinationDatastoreFreeSpace = [Math]::Round(($destinationDatastoreFreeSpace.”FreeSpace” / 1GB),2)
# Select the host with the less used memory
$DestinationHost = Get-Cluster –Name $DestinationCluster –Server $DestinationVC | Get-VMhost -State Connected | Sort-Object -Property MemoryUsageGB | Select-Object -First 1
# Change Here if you have a vm with multiple Network Cards (Remove the # for the multiple nics)
if ($NetworkAdapter.Count-eq 1) {
$DestinationVMPortgroup =@()
$DestinationVMPortgroup += Get-VirtualPortGroup -Server $DestinationVC -Vmhost $DestinationHost | Out-GridView -OutputMode Single -Title “Select Nic1”
}
elseif ($NetworkAdapter.Count-eq 2) {
$DestinationVMPortgroup =@()
$DestinationVMPortgroup += Get-VirtualPortGroup -Server $DestinationVC -Vmhost $DestinationHost | Out-GridView -OutputMode Single -Title “Select Nic1”
$DestinationVMPortgroup += Get-VirtualPortGroup -Server $DestinationVC -Vmhost $DestinationHost | Out-GridView -OutputMode Single -Title “Select Nic2”
}
elseif ($NetworkAdapter.Count-eq 3) {
$DestinationVMPortgroup =@()
$DestinationVMPortgroup += Get-VirtualPortGroup -Server $DestinationVC -Vmhost $DestinationHost | Out-GridView -OutputMode Single -Title “Select Nic1”
$DestinationVMPortgroup += Get-VirtualPortGroup -Server $DestinationVC -Vmhost $DestinationHost | Out-GridView -OutputMode Single -Title “Select Nic2”
$DestinationVMPortgroup += Get-VirtualPortGroup -Server $DestinationVC -Vmhost $DestinationHost | Out-GridView -OutputMode Single -Title “Select Nic3”
}
elseif ($NetworkAdapter.Count-eq 4) {
$DestinationVMPortgroup =@()
$DestinationVMPortgroup += Get-VirtualPortGroup -Server $DestinationVC -Vmhost $DestinationHost | Out-GridView -OutputMode Single -Title “Select Nic1”
$DestinationVMPortgroup += Get-VirtualPortGroup -Server $DestinationVC -Vmhost $DestinationHost | Out-GridView -OutputMode Single -Title “Select Nic2”
$DestinationVMPortgroup += Get-VirtualPortGroup -Server $DestinationVC -Vmhost $DestinationHost | Out-GridView -OutputMode Single -Title “Select Nic3”
$DestinationVMPortgroup += Get-VirtualPortGroup -Server $DestinationVC -Vmhost $DestinationHost | Out-GridView -OutputMode Single -Title “Select Nic4”
}
LogWrite “Start move: $vm”
Logwrite “VM IP: $vmip”
Logwrite “VM Disk Used (GB): $VMHDDSize”
Logwrite “VM Folder: $vmfolder”
Logwrite “Source vCenter: $SourceVC”
Logwrite “VM Source Cluster: $SourceCluster”
Logwrite “Destination vCenter: $DestinationVC”
Logwrite “VM Destination Cluster: $DestinationCluster”
Logwrite “Destination host: $DestinationHost”
LogWrite “VM Source PortGroup: $SourceVMPortGroup”
LogWrite “VM Destination Portgroup: $DestinationVMPortgroup”
Logwrite “VM Destination Datastore: $destinationDatastore”
LogWrite “Destination Datastore FreeSpace GB: $destinationDatastoreFreeSpace “
if ( $Dryrun ) {
$FreespaceAfterMigration = $destinationDatastoreFreeSpace – $VMHDDSize
if ( $FreespaceAfterMigration -lt 0 ) { Logwrite “ERROR: Datastore $destinationDatastore does not have sufficient freespace! Virtual Machine needs $VMHDDSize. Only $destinationDatastoreFreeSpace available.” }
else { Logwrite “Virtual Machine will fit on datastore $destinationDatastore. Freespace after migration is: $FreespaceAfterMigration GB” }
}
#Test if VM responsed to ping
if ($vmip -eq $null) {
LogWrite “Virtual Machine ip address not known”
Logwrite “No ping check will be performed after moving the Virtual Machine”
}
else {
Test-Connection -comp $vmip -quiet
LogWrite “Virtual Machine $vm response to ping before being moved. Virtual machine will be checked after being moved”
$PingVM = $true
}
#if ( $VMHDDSize -eq
if ( -NOT $Dryrun) {
#Migrate VM to cluster
LogWrite “Move $vm to vCenter $DestinationVC and datastore $DestinationDatastore”
Try {
$Result = Move-VM -VM $vm `
-Destination $DestinationHost `
-Datastore $DestinationDatastore `
-NetworkAdapter $NetworkAdapter `
-PortGroup $DestinationVMPortgroup `
-ErrorAction Stop
}
Catch {
$ErrorMessage = $_.Exception.Message
LogWrite “ERROR: Move of $vm to cluster $DestinationHost failed!!!”
Logwrite “ERROR: Move Status Code: $ErrorMessage”
SendWhatsApp “ERROR: Move of $vm failed!!! $ErrorMessage”
$MigError = $true
}
#Migrate VM to folder
LogWrite “Move $vm to vCenter $vmfolder”
Try {
$VMtemp = get-vm $vm
$Result = Move-VM -VM $vmtemp -InventoryLocation $vmfolder -ErrorAction Stop
}
Catch {
$ErrorMessage = $_.Exception.Message
LogWrite “ERROR: Move of $vm to folder $vmfolder failed!!!”
Logwrite “ERROR: Move Status Code: $ErrorMessage”
SendWhatsApp “ERROR: Move of $vm failed!!! $ErrorMessage”
$MigError = $true
}
}
$MigError = $false
#Test if VM is running on destination cluster
if ( -NOT $MigError -AND -NOT $Dryrun ) {
LogWrite “Check $vm is registered in $DestinationVC”
try {
$CheckVM = get-vm -name $vm -server $DestinationVC -ErrorAction Stop
if ( $CheckVM ) {
Logwrite “$vm registered in $DestinationVC”
}
else {
Logwrite “ERROR: $vm not found in $DestinationVC”
}
}
catch {
$ErrorMessage = $_.Exception.Message
Logwrite “ERROR: $vm not found in $DestinationVC”
Logwrite “ERROR: $ErrorMessage”
SendWhatsApp “ERROR move: $vm not found in $DestinationVC”
}
}
#Test is VM response to ping, if $PingVM = $True
if ($PingVM) {
if (Test-Connection -comp $vmip -quiet) {
LogWrite “Virtual Machine $vm response to ping after move”
SendWhatsApp “Virtual Machine $vm response to ping after move”
}
}
sleep 1
SendWhatsApp “Finished move action: $vm from $SourceVC to $DestinationVC”
Logwrite “Finished move action: $vm from $SourceVC to $DestinationVC”
if ($DRSRecommendation)
{
Get-DrsRecommendation -Cluster $DestinationCluster -Server $DestinationVC | Apply-DrsRecommendation
Logwrite “DRS Recommendatation applyed”
}
Else
{
Logwrite “No DRS Recommendatation applyed”
Write-Host “No DRS Recommendatation applyed”
}
#Disconnect from vCenter servers
Logwrite “Disconnect from vCenter servers $SourceVC $DestinationVC”
Disconnect-viserver $SourceVC -Confirm:$false
Disconnect-viserver $DestinationVC -Confirm:$false
Logwrite “Finished moving virtual machines, exiting…..”
SendWhatsApp “Finished moving virtual machines, exiting…..”
Like this:
Like Loading...
You must be logged in to post a comment.