Virtual machine migration via Windows PowerShell scripts

These Windows PowerShell scripts will help solutions providers to ensure that the process for both single and multiple virtual machine migrations will be completed quickly.

Solutions provider takeaway: Virtual machine migration (for both single and multiple machines) can be a tedious task for solutions providers, but these Windows PowerShell scripts will make their jobs easier by using a clusters or storage area networks for migration.

About the book:
This chapter excerpt on Automation Using PowerShell (download PDF) is taken from the book Mastering Virtual Machine Manager 2008 R2. This book offers information on the features, capabilities and architecture that solutions providers need for a successful Virtual Machine Manager (VMM) 2008 R2 deployment. You will find out the best ways to accomplish tasks such as deploying virtual machines to Hyper-V hosts or backing up and planning for recovery of the VMM server.

Virtual Machine Migrations

When you're migrating a virtual machine through Virtual Machine Manager, the transfer type (or speed) for the migration is determined during Intelligent Placement and it is based upon the properties and capabilities of the source virtual machine and the destination host along with the connectivity between them. In Listing 8.5, we attempt to migrate a virtual machine while enforcing a requirement that only SAN or cluster migrations are eligible. This puts a requirement on the script to not only check for good host ratings but also to ensure that the transfer can be accomplished quickly using a SAN or a failover cluster.

Listing 8.5: Migrating a virtual machine using cluster or SAN and tracking the migration progress

# Get a connection to the VMM server
$c = Get-VMMServer "localhost"

# Get the VM to migrate
$VM = Get-VM "virtualmachine1"

# Get all the hosts
$AllHosts = Get-VMHost

# The IsMigration flag allows the current host of the VM
# to be considered as the migration target
$hostrating = Get-VMHostRating -VMHost $AllHosts -VM $VM -IsMigration

# Order the host ratings
$orderedrating = $hostrating | sort-object rating -descending
Write-Output $orderedrating

# Now search for the top rated host that can do a Cluster or a SAN migration
# All other migration options are not considered here
$targethost = $null
foreach ($rating in $orderedrating)
{
if ($rating.Rating -gt 0)
{
switch ($rating.TransferType)
{
# These options are listed in order of decreasing transfer speed
"Live"
{
Write-Output "$rating.Name has a transfer type of Live"
$targethost = $rating.Name
break
}
"Cluster"
{
Write-Output "$rating.Name has a transfer type of Cluster"
$targethost = $rating.Name
break
}
"San"
{
Write-Output "$rating.Name has a transfer type of SAN"
$targethost = $rating.Name
break
}
"Network"
{
Write-Output "$rating.Name has a transfer type of Network"
}
default
{
Write-Output "$rating.Name has an invalid TransferType"
}
}
}

}
if ($targethost -eq $null)
{
Write-Warning "We were not able to find a suitable destination
host for this VM with a fast transfer (SAN or Cluster)"

 break
}

# Migrate the VM to the target host
$VMHost = Get-VMHost -ComputerName $targethost
$resultvm = Move-VM -VM $VM -vmhost $VMHost -Path $VMHost.VMPaths[0]
-RunAsynchronously

# Get the VMM Job that was launched for this migration $job = $resultvm.MostRecentTask
# Iterate the loop until the Job is finished while reporting progress while ($job.Status -eq "Running")
{
$progress = $job.Progress
Write-Progress $VM Progress -PercentComplete $job.ProgressValue -ID 1
Start-Sleep 3
}

# The VMM job is now finished (either with a failed or a completed status)
$status = $job.Status
Write-Warning "Migration of $VM to host $VMHost finished
with a status of: $status"
$error = $job.ErrorInfo | select DisplayableErrorCode,
Problem, RecommendedActionCLI
Write-Warning $error

In some cases, it is beneficial to force a LAN migration even when a faster migration option is available. The only change from the code in Listing 8.5 would be a small change in the Move-VM cmdlet to add the UseLAN option as indicated here.

# Use the -UseLAN option to force a Network transfer of the Virtual Machine
$resultvm = Move-VM -VM $VM -vmhost $VMHost -Path $VMHost.VMPaths[0]
-RunAsynchronously -UseLAN

Provisioning Multiple Virtual Machines

Virtual machines are usually provisioned on demand based on customer requirements. However, there are cases where having many virtual machines available for immediate use is a requirement. Such scenarios might include hosted desktops allocating virtual machines from a pool of VMs or allocating VMs to an enterprise application based on load. In the PowerShell script in Listing 8.6, we read the input from a text file and create virtual machines in blocks of five at a time (the throttling rate is customizable). This allows us to customize and repeat the automated provisioning process by updating a text file rather than having to adjust a PowerShell script.

Listing 8.6: PowerShell script for creating multiple VMs based on an input file

# get the command line arguments passed to this script
$length = $args.length
$expectedArgsLength = 2

# The script takes as input the customization filepath and the VMM server name
$usage = "Usage: ScriptName.ps1 "
if ($length -ne $expectedArgsLength)
{
write-warning $usage;
break
}

# The ArrayList to use for tracking new-vm creations
$arraylist = New-Object System.Collections.ArrayList
$arraylist.Clear()

# The max number of concurrent VM creations (throttling rate)
$MaxCreations = 5

About the authors:

Michael Michael is a software architect on Microsoft's virtualization and data center management team and is working on the development and future direction of System Center Virtual Machine Manager (SCVMM). Michael is primarily focused on partner interactions and ensuring that a healthy ecosystem exists for virtualization products. He also has his own blog on virtual machine management.

Hector Linares is a program manager on Microsoft's virtualization and data center management team, and develops SCVMM in the management and solutions division at Microsoft.

# Get a connection to the VMM server
$servername = $args[1]
get-vmmserver -ComputerName $servername

# now open the customization file to read its input
$customFile = $args[0]
$content = get-content $customFile
foreach ($values in $content)

{
# $values contains one line of input. Each line represents a VM
# now split the CSV input line
$newvalues = $values |% {$_.split(",")}

# Perform a test to ensure the proper number of parameters exist
if ($newvalues.length -ne 14)
{
write-warning "The proper number of parameters does not exist for $values";
break
}

#  get  the  input  variables  from  the  file  and  into  the  specific  variables
$vmname = $newvalues[0] # The virtual machine name

$computername = $newvalues[1] # The guest OS computer name
$memory = $newvalues[2] # The amount of RAM to allocate to the VM
$OSSKU = $newvalues[3] # The OS name (VMM has these
already defined)
$ProductID = $newvalues[4] # The Windows Product ID
$description = $newvalues[5] # A description for the VM
$vmpath = $newvalues[6] # The path where to create this VM
$vnetworkname = $newvalues[7] # The Virtual Network Name
$hostname = $newvalues[8] # The name of the host to place this VM on
$cpuvalue = $newvalues[9] # The CPU Name (VMM has these
already defined)
$cpucount = $newvalues[10] # The number of CPUs
$owner = $newvalues[11] # The owner of the VM
$adminpwd = $newvalues[12] # The guest OS administrator password
$templatename = $newvalues[13] # The template name from
which to create this VM

# Create the Job Group ID and the hardware profile name
$jobguid = [guid]::NewGuid().ToString()
$profilename = "Profile" + $jobguid

# create the VM based on the settings in the file - this will happen asynchronously

Set-VirtualFloppyDrive -RunAsynchronously -VMMServer $servername
-NoMedia -JobGroup $jobguid
New-VirtualNetworkAdapter -VMMServer $servername -JobGroup
$jobguid -PhysicalAddressType Dynamic -VirtualNetwork $vnetworkname
-VLanEnabled $false
New-VirtualDVDDrive -VMMServer $servername -JobGroup $jobguid -Bus 1 -LUN 0
$CPUType = Get-CPUType -VMMServer $servername | where {$_.Name -eq $cpuvalue}
New-HardwareProfile -VMMServer $servername -Owner $owner
-CPUType $CPUType -Name $profilename -Description "Profile used to
create a VM/Template" -CPUCount $cpucount -MemoryMB
$memory -ExpectedCPUUtilization 20 -DiskIO 0 -NetworkUtilization
10 -RelativeWeight 100 -HighlyAvailable $false -NumLock $false
-BootOrder "CD", "IdeHardDrive", "PxeBoot", "Floppy"
-LimitCPUFunctionality $false -JobGroup $jobguid
$Template = Get-Template -VMMServer $servername | where
{$_.Name -eq $templatename}
$VMHost = Get-VMHost -VMMServer $servername | where {$_.Name -eq $hostname} $HardwareProfile = Get-HardwareProfile -VMMServer localhost |

where {$_.Name -eq $profilename}
$OperatingSystem = Get-OperatingSystem -VMMServer localhost |
where {$_.Name -eq $OSSKU}

# Before we start the new-vm creation we need to check

# if we reached the maximum number of concurrent creations while ($arraylist.Count -eq $MaxCreations)
{
$toremove = $null
foreach ($jobid in $arraylist)
{
# get the current status of the job
$tempjobid = [string]::join("", $jobid.Keys)
$tempjob = Get-Job -ID $tempjobid;
if ($tempjob.Status -ne "Running")
{
# This job completed, so remove it from the tracking list
so that new VMs can be created
Write-Output "Job $tempjobid finished running"
$toremove = $jobid
break
}
}

if ($toremove -ne $null)
{
$arraylist.Remove($jobid)
}

Start-Sleep 2
}

# if we reached here, it is safe to create the new VM
$resultvm = New-VM -Template $Template -Name $vmname
-Description $description -VMHost $VMHost -Path $vmpath -JobGroup
$jobguid -Owner $owner -HardwareProfile $HardwareProfile
-ComputerName $computername -FullName "" -OrgName "" -ProductKey
$ProductID -TimeZone 4 -JoinWorkgroup "WORKGROUP" -OperatingSystem
$OperatingSystem -RunAsSystem -StartAction
NeverAutoTurnOnVM -UseHardwareAssistedVirtualization $false
-StopAction SaveVM -RunAsynchronously

# Now start tracking this new-vm instance
if ($resultvm -ne $null)
{
# Get the VMM Job that was launched for this migration

$job = $resultvm.MostRecentTask
$arraylist.Add(@{$job.ID = $job})
}
}

write-output "Done creating All VMs!"

The following code contains a sample line from an input text file that can be used in the script in Listing 8.6. This line contains the values for the different virtual machine properties that are needed by the PowerShell script. These values need to be specified in order, and their descriptions are as follows:

  1. The virtual machine name
  2. The guest OS computer name
  3. The amount of RAM or memory to allocate to the VM
  4. The OS name (VMM has these already defined)
  5. The Windows product ID
  6. A description for the VM
  7. The path describing where to create this VM
  8. The virtual network name
  9. The name of the host on which to place this VM
  10. The CPU name (VMM has these already defined.)
  11. The number of CPUs
  12. The owner of the VM
  13. The guest OS administrator password
  14. The name of the template from which to create this VM

vmname1,vmname1ComputerName,1024,64-bit edition of Windows Server 2008
Enterprise,55555-55555-55555-55555-55555,scripted VM,D:ProgramDataMicrosoft
WindowsHyper-V,Broadcom NetXtreme 57xx Gigabit Controller - Virtual Network,hypervhost1.vmmdomain.com,2.40 GHz Xeon,1,vmmdomainadministrator,
password,MyTemplate


Automation Using PowerShell
  Using Windows Powershell scripts for task automation
  Virtual machine migration via Windows PowerShell scripts
  New virtual machine management using VMM, PowerShell

Printed with permission from Wiley Publishing Inc. Copyright 2009. Mastering Virtual Machine Manager 2008 R2 by Michael Michael and Hector Linares. For more information about this title and other similar books, please visit Wiley Publishing Inc.

Dig Deeper on MSP technology services