Creating Reusable PowerShell Scripts Using Hyper-V PowerShell Module Cmdlets - Microsoft Hyper-V PowerShell Automation (2015)

Microsoft Hyper-V PowerShell Automation (2015)

Chapter 4. Creating Reusable PowerShell Scripts Using Hyper-V PowerShell Module Cmdlets

In this chapter, we will look at how to create reusable PowerShell scripts for day-to-day Hyper-V management activities using cmdlets. We will be utilizing the core cmdlets that we learned in the previous two chapters to create these reusable scripts. We have subdivided this chapter into four sections, which will cover the core automation strategies that can be used to manage repetitive administrative tasks:

· Creating reusable scripts for virtual machine creation utilizing offloaded data transfers (ODX)

· Creating reusable scripts for virtual machine live migration

· Creating reusable scripts to manage a virtual machine's snapshots, export, and import

· Creating reusable scripts to automate installation of Integration Service in virtual machines

Creating reusable scripts for virtual machine creation utilizing ODX

Using the core virtual machine cmdlets included in Hyper-V PowerShell, we can automate the process of virtual machine creation. With Windows Server 2012, Windows Server 2012 R2, and System Center Virtual Machine Manager 2012 R2, we can speed up the process of virtual machine provisioning utilizing ODX, so let's look at what exactly ODX is.

ODX is a new technology feature supported by the latest Windows Server 2012 and Windows Server 2012 R2 operating systems that offloads the standard copy operations from Windows networks to the underlying storage system. For example, a virtual hard disk, when copied over the network for a virtual machine provisioning process, would take hours to complete the copy process depending on the virtual hard disk size as the copy process takes place over the network.

ODX is enabled by default in Windows Server 2012 and Windows Server 2012 R2. You can check whether it's enabled or disabled using a simple registry key check using PowerShell:

1. In a PowerShell prompt with administrative rights, execute the following command:

2. Get-ItemProperty hklm:\system\currentcontrolset\control\filesystem -Name "FilterSupportedFeaturesMode"

As you can see, when we execute this command on the test server, it shows that ODX is disabled since the value is set to one:

Creating reusable scripts for virtual machine creation utilizing ODX

3. Now, to enable ODX, we need to set this registry key value to 0.

4. Execute the following PowerShell command to enable ODX:

5. Set-ItemProperty hklm:\system\currentcontrolset\control\filesystem -Name "FilterSupportedFeaturesMode" -Value 0 –Type Dword

Creating reusable scripts for virtual machine creation utilizing ODX

6. Now, let's explore a PowerShell script that you can reuse in your environment to automate the creation of virtual machines using ODX. We will be using the concept of PowerShell remoting to do this. We have explained the various cmdlets in the following code, which when executed together as a PowerShell script will help you to automate the virtual machine provisioning process. I have broken the script into small sections explaining in detail what each cmdlet is expected to do when it is executed:

· Enter the following command in a PowerShell window:

· ########################################################

· #Create Credential Objects to Connect to the Hyper-V Host

· ########################################################

· $password = Read Host -AsSecureString

· $credential = New-Object System.Management.Automation.PsCredential("HVHOST\admin",$password)

Using the preceding piece of code, we created a variable called $password into which we saved a predefined password to a secure string using the ConvertTo-SecureString cmdlet. Once we created a secured password, we created a $credential variable that contains the credential object. This way of accepting credentials allows the administrator to save the credentials in a variable that can be reused, and it avoids the usage of the Get-Credential cmdlet, which gives a pop up every time a user tries to authenticate to a system or the Hyper-V host:

· In this section, we will create a remote PowerShell session to the Hyper-V host using the credentials that we saved in the $credential variable. We will also use the CredSSP-based authentication mechanism:

· ########################################################

· #Create PowerShell Remote sessions to the Hyper-V Host

· ########################################################

· $session = New-PSSession -ComputerName <Hyper-V Host Name> -credential $credential

· Invoke-Command -Session $session -ScriptBlock {

· In this section, we will create a new directory using the New-Item cmdlet to save the virtual machine in a clustered shared storage location; this will contain the virtual machine configuration file and its virtual hard disk:

· ########################################################

· #Create a New directory to save the virtual machine contents

· ########################################################

·

· New-Item -Path "C:\ClusterStorage\Volume1\VMS" -Name <Virtual Machine Name> -ItemType directory -Force

· In this section, we will use the magic of ODX by copying over the virtual machine hard disk to the destination virtual machine location using the Copy-Item cmdlet. Now, as I have ODX enabled, the Copy-Item cmdlet copies across the virtual machinessuper-fast to the destination location utilizing the underlying storage technologies:

· #####################################################################

· #Use ODX based copy process to copy across the VHD in seconds

· ####################################################################################

· Copy-Item "C:\ClusterStorage\Volume1\vhd\win2k12r2_sysprepped.vhdx" "C:\ClusterStorage\Volume1\VMS\<Virtual Machine Name>"

· In this section, we will change the current directory to the location where we copied over the virtual machine hard disk. We will rename the virtual hard disk to a more user friendly name, which will consist of the virtual machine's name for easy administration, using the Set-Location cmdlet:

· Set-Location "C:\ClusterStorage\Volume1\VMS\<Virtual Machine Name>"

· $VMPath = "C:\ClusterStorage\Volume1\VMS\<Virtual Machine Name>"

· Rename-Item win2k12r2_sysprepped.vhdx -NewName "<Virtual Machine Name>-OS.vhdx"

· In this section, we will create the virtual machine using the New-VM cmdlet and assign the virtual machine a memory of 4 GB and the virtual machine hard disk path and virtual machine path of $VHDPath and $VMPath respectively, and also a virtual switch named "Virtual Switch V1":

· ##############################################################################

· #Create Virtual Machine on the Hyper-V host and Configure its properties

· ##############################################################################

· Import-Module hyper-v

·

· $VHDPath = "C:\ClusterStorage\Volume1\ VMS\<Virtual Machine Name>/"<Virtual Machine Name>-OS.vhdx "

·

· New-VM -ComputerName $PH -MemoryStartupBytes 4GB -Name <Virtual Machine Name>-VHDPath $VHDPath -Path $VMPath -SwitchName "Virtual Switch V1"

·

·

· #########################################################################################

· #Set Vm properties before start, enable dynamic memory for memory optimization

· Set-VM -name <Virtual Machine Name>-ProcessorCount 4 -DynamicMemory -AutomaticStartAction StartIfRunning -AutomaticStopAction Save

·

· #########################################################################################

· # Disable Time Sync on a VM so that it sync's time froma domain controller

· Disable-VMIntegrationService -VMName <Virtual Machine Name> -Name "Time Synchronization"

·

· Start-VM <Virtual Machine Name> -Verbose

Once the virtual machine gets created, we can use the Set-VM cmdlet to set the virtual machine properties such as processor count, dynamic memory, and the automatic start and stop action. We will also disable the "Time Synchronization" VM Integration Service so that the virtual machine does not sync its clock with the time set on the Hyper-V host. Once all the configuration activities are completed, we will start the virtual machine using the Start-VM cmdlet:

####################################################################################

#Make the Virtual Machines Highly Available and add them to the clustered instance

####################################################################################

Import-Module failoverclusters

Add-ClusterVirtualMachineRole -VirtualMachine <Virtual Machine Name>

7. Next, we will make the virtual machine highly available using the Add-ClusterVirtualMachineRole cmdlet so that its services remain highly available in the event of a host Hyper-V virtual machine crash.

Creating reusable scripts for virtual machine live migration

Reusable scripts help the Hyper-V administrator to automate various mundane tasks. Let's explore ways to automate one of the most commonly used virtual machine tasks. To do this, let's look at a script that can be used to automate the live migration of virtual machines across various Hyper-V hosts in a cluster.

Similar to the previous script, let's break this script into various components to understand its execution step by step. Also, in the scripting technique illustrated as follows, we will be using the concept of PowerShell workflows to migrate the virtual machines across the Hyper-V host cluster live in a parallel manner and not a sequential one:

workflow Move-LiveVM

{

param(

[Parameter(Mandatory)]

[string]$SourceHyperVhost,

[Parameter(Mandatory)]

[string]$DestinationHyperVhost,

[Parameter(Mandatory)]

[string]$ClusterName

)

Using the preceding piece of code, we created a PowerShell workflow called Move-LiveVM, which gets its input from the $SourceHyperVhost (which is the source Hyper-V host name), $DestinationHyperVhost (which is the destination Hyper-V host name), and $clustername(which is the Hyper-V cluster name) parameters. All these are mandatory parameters and need to be input passed by the user during workflow execution:

$vminfo = Get-ClusterGroup -cluster $clustername | Where-Object -filterscript {$_.grouptype -match "VirtualMachine" -and $_.ownernode -match $SourceHyperVhost}

Foreach -parallel ($vm in $vminfo)

{

Move-ClusterVirtualMachineRole $vm -Node $DestinationHyperVhost -MigrationType live -Cluster $clustername

}

}

Next, we extracted the virtual machine cluster group name for all the virtual machines that were highly available and used a for-each loop with the – parallel parameter to move the virtual machines to the destination Hyper-V host using live migration in parallel.

Creating reusable scripts to manage export and import of virtual machine snapshots

Next, we will look at some scripts that can be used to automate the virtual machine import, export, and snapshot processes. This section is relatively simple as we will be using a for-each loop to iterate across all virtual machines and perform these activities.

For this particular example, we will be using a single script to illustrate all three processes:

$vminfo = Get-VM

As you can see in the preceding piece of code, we will extract the information about all the virtual machines and store it in a variable, $vminfo:

Foreach ($vm in $vminfo)

{

Next, let's iterate across all the virtual machines stored in the $vminfo variable and create a snapshot of the virtual machines using the Checkpoint-VM cmdlet:

Checkpoint-VM -Name $vm.name -SnapshotName BeforeInstallingUpdates

}

Next, let's export all the virtual machines to a location using the Export-VM cmdlet. We can use a similar for-loop technique to export all the virtual machines in our Hyper-V server to a predefined location:

$vminfo = Get-VM

Foreach ($vm in $vminfo)

{

Export-VM –Name $vm.name –Path D:\Export

}

Once we export all these virtual machines to a predefined location, we can also import them using the Import-VM cmdlet. We can again use a similar for loop technique to import the virtual machines:

$vminfo=GET-CHILDITEM D:\Export -recurse –include *.exp

Through the preceding piece of code, we can get the details of all the virtual machines that have been exported in our example:

$VMinfo | FOREACH {

IMPORT-VM -path $_.Fullname -Copy -VhdDestinationPath $VMDefaultDrive –

VirtualMachinePath $VMDefaultPath -SnapshotFilePath $VMDefaultPath -SmartPagingFilePath $VMDefaultPath -GenerateNewId

}

In the preceding code snippet, we iterate across all the virtual machines that have been exported and import them into Hyper-V Manager from the exports.

Creating reusable scripts to automate installation of Integration Service in virtual machines

Next, we will look at some scripts that can be used to automate the installation of Integration Service in virtual machines. These scripts support Windows Server 2012, Hyper-V Version 3, and their later versions from the perspective of Microsoft hypervisor.

This example is illustrated as follows by creating a function that gets Integration Service installed.

Let's consider four parameters for this function, which include the virtual machine name, the Hyper-V hostname, the username, and the password:

function Install-VMIntegrationService

{

[CmdletBinding()]

Param

(

# Param1 help description

[Parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$true,Position=0)]

$VMName,

# Param2 help description

[Parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$true,Position=1)]

$VMComputerName,

# Param2 help description

[Parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$true,Position=2)]

$username,

# Param2 help description

[Parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$true,Position=3)]

$password

)

Next, let's iterate across all the virtual machines and their associated Hyper-V host using multiple for-each loops. Consider four parameters for this function, which include the virtual machine name, the Hyper-V hostname, the username, and the password:

foreach ($vm in $vmname)

{

foreach ($comp in $VMComputerName)

{

Next, we will create a credential object, which will be used to invoke remote PowerShell sessions in the virtual machines to get the Integration Service version.

$pass = ConvertTo-SecureString -String $password -AsPlainText -force

$cred = New-Object System.Management.Automation.PsCredential($username,$pass)

Next, we will mount the VMGuest.iso image to a virtual machine and compare the versions of Integration Service on the Hyper-V host and the virtual machine. I also extract the DVD drive letter:

Set-VMDvdDrive -VMName $vm -Path "C:\Windows\System32\vmguest.iso"

$DVDriveLetter = Get-VMDvdDrive -VMName $vm | select -ExpandProperty id | Split-Path -Leaf

$HostICversion= Get-ItemProperty "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Virtualization\GuestInstaller\Version" | select -ExpandProperty Microsoft-Hyper-V-Guest-Installer

$VMICversion = Invoke-Command -ScriptBlock {Get-ItemProperty "HKLM:\software\microsoft\virtual machine\auto" | select -ExpandProperty integrationservicesversion } -ComputerName $comp -Credential $cred

If we find that the versions of Integration Service on both the virtual machine and the Hyper-V host are the same, then we will unmount the VMGuest.iso DVD drive from the virtual machine, exit the script, and write a verbose message to the user informing him or her that the Integration Service version is up-to-date on the virtual machine:

if($HostICversion -eq $VMICversion) {

Write-Verbose "Hyper-V Host IC Version and the VM $vm IC Version are the same" -Verbose

$obj = New-Object psobject -Property @{

'HostIntegration Services Version' = $HostICversion

'VMIntegration Services Version' = $VMICversion

'Hyper-V Host Name' = hostname

'VirtualMachine Name'= $vm

}

Write-Output $obj

Set-VMDvdDrive -VMName $vm -ControllerNumber1 -ControllerLocation 0 -Path $null

}

If we find that the versions of Integration Service on both the virtual machine and the Hyper-V host are different, we will display a message to the user stating that the virtual machine has the old version of Integration Service:

else {

$VMICversion = Invoke-Command -ScriptBlock {Get-ItemProperty "HKLM:\software\microsoft\virtual machine\auto" | select -ExpandProperty integrationservicesversion } -ComputerName $comp -Credential $cred

write-verbose "$vm Old Integration Services Version $VMICversion" -Verbose

Next, we will use the Invoke-WMIMethod cmdlet to install Integration Service silently on the virtual machine; we will allow this command to enter sleep mode for 3 seconds before execution:

Invoke-WmiMethod -ComputerName $comp -Class Win32_Process -Name Create -ArgumentList "$($DVDriveLetter):\support\x86\setup.exe /quiet /norestart" -Credential $cred

start-sleep 3

Next, we will use a while loop to check whether the process that started at the time of the installation of Integration Service was completed successfully; we will also display a message showing the progress of installation:

while (@(Get-Process setup -computername $comp -ErrorAction SilentlyContinue).Count -ne 0) {

Start-Sleep 3

Write-verbose "Waiting for Integration Service Install to Finish on $comp ..." -Verbose

}

Once the script verifies that Integration Service has been installed, we will have to restart the computer for the changes to take place. In the end, we will again compare the Integration Service version on both the virtual machine and the Hyper-V host, verify that it is the same, and give a user friendly output to the user stating the installation has been completed:

write-verbose "Completed the Installation of Integration Services" -Verbose

write-verbose "Restarting Computer for Changes to Take Place" -Verbose

Restart-Computer -ComputerName $comp -Wait -For WinRM -Force -Credential $cred

write-verbose "$vm Is Online Now" -Verbose

$VMICversion = Invoke-Command -ScriptBlock {Get-ItemProperty "HKLM:\software\microsoft\virtual machine\auto" | select -ExpandProperty integrationservicesversion } -ComputerName $comp -Credential $cred

write-verbose "$vm New Integration Services Version $VMICversion" -Verbose

Set-VMDvdDrive -VMName $vm -ControllerNumber 1 -ControllerLocation 0 -Path $null

}

}

}

}

Summary

In this chapter, we went in-depth into ways to build custom scripts for various day-to-day administrative activities. In the next chapter, we will cover in detail how to troubleshoot Hyper-V environment issues using the best practices for PowerShell cmdlets in Hyper-V.