Chapter 8
Configuring Virtual Machine Hardware

In this chapter, you will learn to:

  • Add, Configure, and Remove Virtual Hardware
  • Changing Virtual Memory
  • Changing Memory Resources
  • Changing the Number of vCPUs
  • Changing vCPU Resources
  • Adding or Removing a Network Adapter
  • Assigning a Network
  • Adding a Virtual Disk
  • Removing a Virtual Disk
  • Extending a Virtual Disk
  • Changing Other Hardware
  • Optimize Storage Usage with Thin Provisioning
  • Converting a Virtual Disk Using Storage vMotion
  • Converting a Virtual Disk in Place

Once the virtualization environment is up and running, there comes a time when a virtual machine needs to be reconfigured. Perhaps performance is lacking and an additional vCPU or more memory is needed. Or a disk might be running into its capacity limit and needs to be extended. All of these tasks and some other reconfiguration tasks are covered in this chapter.

Add, Configure, and Remove Virtual Hardware

This section will focus on changing virtual machine (VM) hardware configurations like memory, vCPU, network adapters, and virtual disks.

Changing Virtual Memory

It’s a story as old as virtualization itself. A VM is pushed into production with 1,024 MB of RAM, and you quickly notice that performance is lacking due to memory resource exhaustion. The fix is simple: increase the amount of memory on the VM. To do so, use the Set-VM cmdlet. This cmdlet accepts a -MemoryMB parameter, which allows modification of the VM’s memory size:

Set-VM VM001 -MemoryMB 2048 -Confirm:$false

Like everything else in vSphere, the ability to modify a VM is based on the hardware version rules and the VM configuration. The VM in question must either be powered off or support memory hot plug. If the VM doesn’t support memory hot plug and the VM can only be shut down during maintenance hours, it might be advantageous to schedule the upgrade process.

Listing 8-1 automates the complete process. First, the VM is gracefully shut down, and after the memory is upgraded, the VM is powered on again. Using this simple script, it is possible to schedule the upgrade during maintenance hours when the impact on end users is minimal.

Listing 8-1: Changing virtual machine memory offline

Get-VM VM001 | Stop-VMGuest -Confirm:$false
While ((Get-VM VM001).PowerState -ne "PoweredOff"){
  Sleep -Seconds 2
}
Get-VM VM001 | Set-VM -MemoryMB 2048 -Confirm:$false
Get-VM VM001 | Start-VM -Confirm:$false

Changing Memory Resources

To guarantee memory entitlement, a memory reservation must be configured for the VM. Memory reservations guarantee that the reserved memory will always be backed by physical machine memory, even during times of memory contention. This is often configured to ensure some level of performance for a VM. Memory reservations also determine the size of the VM’s swap file on the vSphere host, as the swap file’s size is equal to the VM’s configured memory minus its reservation.

There are also times when it is necessary to artificially cap a VM to prevent it from using too much machine memory. This is accomplished by configuring a memory limit. Memory limits force any memory requirements above the limit to be provisioned from the VM’s swap file on the host. Memory provisioned from the swap file doesn’t perform very well and should only be used in situations where extreme memory pressure is degrading the performance of all VMs.

All elements of VM resource configuration are configurable with the Get-VMResourceConfiguration and Set-VMResourceConfiguration cmdlets.

To set a memory reservation, use the -MemReservationMB parameter. The value of this parameter ranges from 0 to the value of the memory limit or, if no limit is set, to the amount of configured memory. To set a memory limit, use the -MemLimitMB parameter. The value for this parameter ranges from the value of the memory reservation to the maximum amount of configured memory. Figure 8-1 is a graphical representation of these parameter ranges.

To set the memory reservation to 1,024 MB and the memory limit to 1,536 MB on your virtual machine, use the following:

Get-VM VM001 | Get-VMResourceConfiguration | '
    Set-VMResourceConfiguration -MemReservationMB 1024 '
    –MemLimitMB 1536
c08f001.eps

Figure 8-1: Memory reservation and limit ranges

To reset the memory limit on a VM (set it to Unlimited), use the following:

Get-VM VM001 | Get-VMResourceConfiguration | '
    Set-VMResourceConfiguration –MemLimitMB $null

The third mechanism to configure memory resources is shares. Shares come into play when there’s memory contention on a host. When memory contention is encountered, shares determine the VM’s entitlement to machine memory for the amount of memory between the reservation and the limit (see Figure 8-1). To change the number of memory shares, use the -MemSharesLevel parameter. Possible enumeration values are Custom, High, Low, and Normal. Because this parameter accepts an enumerator value, it is also possible to specify an index value from 0 to 3, where an index value of 0 means Custom, 1 means High, 2 means Low, and 3 means Normal. Table 8-1 contains an overview of the available memory shares levels. It is rare to need to fine-tune the memory shares beyond the factory presets (High, Low, and Normal), but if you have to, set the share level to Custom and specify the number of shares with the -NumMemShares parameter.

Table 8-1: Memory shares

Shares levelShares/MBIndex
Custom1–1,000,0000
High201
Low52
Normal103

To set the memory share level to a custom value of 1,500, use the following:

Get-VM VM001 | Get-VMResourceConfiguration | '
    Set-VMResourceConfiguration -MemSharesLevel "Custom" '
    -NumMemShares 1500

Changing the Number of vCPUs

After addressing the performance issues by adding additional memory, it wouldn’t be surprising to find that the VM now has increased CPU utilization. To ensure that performance stays at an acceptable level, you can allocate additional CPU to the VM. (Be sure to confirm with the application vendor that the application is multithreaded and can benefit from an extra CPU.) To change the number of vCPUs assigned to a VM, use the Set-VM cmdlet again as with changing the amount of memory. This time, however, use the -NumCpu parameter to specify the new number of vCPUs. To add a vCPU to a VM named VM001, use the following:

Set-VM VM001 –NumCpu 2 –Confirm:$false

Again, remember that the VM needs to support CPU hot plug or be powered off. Listing 8-2 contains a function based on the idea of the code in Listing 8-1 that automates the process of changing the number of vCPUs or amount of memory when a VM doesn’t support hot plug. This kind of meta-scripting is common in PowerShell, and it’s one of the reasons the language is so popular with virtualization administrators.

Listing 8-2: Changing VM memory and vCPU offline

function Set-VMOffline 
{
<#
.SYNOPSIS
  Changes the vCPU and memory configuration of the
  virtual machine Offline
.DESCRIPTION
  This function changes the vCPU and memory configuration of
  the virtual machine Offline
.NOTES
Source:  Automating vSphere Administration
  Authors: Luc Dekens, Arnim van Lieshout, Jonathan Medd,
            Alan Renouf, Glenn Sizemore, Brian Graf,
            Andrew Sullivan.PARAMETER VM
  Specify the virtual machine
.PARAMETER MemoryMB
  Specify the memory size in MB
.PARAMETER NumCpu
  Specify the number of virtual CPUs
.PARAMETER TimeOut
  Specify the number of seconds to wait for the vm to shut down
  gracefully. Default timeout is 300 seconds
.PARAMETER Force
  Switch parameter to forcibly shutdown the virtual machine
  after timeout
.EXAMPLE
  PS> Get-VM VM001 | Set-VMOffline -memoryMB 4096 -numCpu 2 '
      -timeOut 60
#>

  Param (
    [parameter(ValueFromPipeline = $true, Mandatory = $true,
     HelpMessage = "Enter a vm entity")]
    [VMware.VimAutomation.ViCore.Impl.V1.Inventory.VirtualMachineImpl]$VM,
    [int64]$memoryMB,
    [int32]$numCpu,
    [Int32]$timeOut = 300,
    [switch]$force)

  Process {
    if ($memoryMB -or $numCpu) 
    {
      if ((Get-VM $VM).PowerState -eq "PoweredOn") 
      {
        $powerState = "On"
        Stop-VMGuest $VM -Confirm:$false | Out-Null
      }
      $startTime = Get-Date
      while (((Get-VM $VM).PowerState -eq "PoweredOn") -and 
     (((Get-Date) - $startTime).totalseconds -lt $timeOut))
      {
        Start-Sleep -Seconds 2
      }
      if ((Get-VM $VM).PowerState -eq "PoweredOff" -or $force) 
      {
        if ((Get-VM $VM).PowerState -eq "PoweredOn") 
        {
          Write-Warning "The shutdown guest operation timed out"
          Write-Warning "Forcing shutdown"
          Stop-VM $VM -Confirm:$false | Out-Null
        }
        $Splat = @{'VM'=$VM;'Confirm'=$False}
        if ($memoryMB) 
        {
          $Splat['MemoryMB'] = $memoryMB
        }
        if ($numCpu) 
        {
          $Splat['NumCpu'] = $numCpu 
        }
        Set-VM @Splat | Out-Null
        if ($powerState -eq "On") 
        {
            Start-VM $VM 
        }
      }
      else 
      {
        Write-Error "The shutdown guest operation timed out"
      }
    }
    else 
    {
      Write-Error "No value for -memoryMB or -numCpu supplied"
    }
  }
}

Changing vCPU Resources

As with memory resources, CPU resources can be influenced by tuning the CPU resource entitlement of a VM with reservations, limits, and shares. Reservations and limits are specified in megahertz (MHz). Reserves are used to guarantee a minimal level of performance for an important production application and to reserve a certain number of MHz for a particular VM. The reserved MHz will always be available to that application. On the other hand, it may be necessary to prevent that badly behaving legacy application from hogging valuable CPU resources by configuring a CPU limit. To change vCPU reservations, limits, and shares, use the Get-VMResourceConfiguration and Set-VMResourceConfiguration cmdlets as described in the section “Changing Memory Resources.” To set a vCPU reservation, use the -CpuReservationMhz parameter. To set a vCPU limit, use the -CpuLimitMhz parameter.

Use the following to set a vCPU reservation of 4,000 MHz on a VM named VM001:

Get-VM VM001 | Get-VMResourceConfiguration | '
    Set-VMResourceConfiguration -CpuReservationMhz 4000

To change the number of vCPU shares, use the -CpuSharesLevel parameter. Possible enumeration values are Custom, High, Low, and Normal. Because this parameter accepts an enumerator value, you can also specify an index value from 0 to 3, where index value 0 means Custom, 1 means High, 2 means Low, and 3 means Normal. Table 8-2 contains an overview of the available CPU shares levels. To fine-tune the shares level beyond the factory presets (High, Low, and Normal), set the share level to Custom and specify the number of shares with the -NumCpuShares parameter.

Table 8-2: CPU shares

Shares levelShares/vCPUIndex
Custom1–1,000,0000
High2,0001
Low5002
Normal1,0003

To set a vCPU share level to High, use the following:

Get-VM VM001 | Get-VMResourceConfiguration | '
    Set-VMResourceConfiguration -CpuSharesLevel "High"

Resource limits, both CPU and memory, are real performance killers. Memory limits cause the affected VM to start ballooning and swapping in almost all cases. How to generate a report containing all VMs that have CPU and memory limits configured is covered in Chapter 15, “Reporting and Auditing.”

With the primary resources of CPU and memory entitlement configured, it’s time to move on to the VM’s networking hardware.

Adding or Removing a Network Adapter

Networking design requirements are one of the most fluid elements of a virtual infrastructure. Often network adapters must be added and reassigned almost on demand. Additional network adapters can be added to an existing VM with the New-NetworkAdapter cmdlet. This cmdlet has undergone several iterations as the ability to manage distributed virtual switches was added to PowerCLI. To maintain backward compatibility, the old parameters have been maintained, but when writing code today the ConnectToPortgroup parameter set should be used because it’s fully compatible with every network technology in vSphere. When using this parameter set, keep in mind the two mandatory parameters:

  • The -VM parameter specifies the virtual machine(s) you want to modify.
  • The -Portgroup parameter specifies the port group that the network adapter will be connected to.

Port group names are case sensitive.

By default a newly created network adapter isn’t connected to the network at startup unless the -StartConnected switch is applied. The adapter type can be specified using the -Type parameter. As of this writing, valid types included 1000, c10e000e, Flexible, Vmxnet, and EnhancedVmxnet. If this parameter isn’t specified, the type will be set as recommended by VMware for the guest OS you set.

To create a second network adapter for VM001 and connect that network adapter to a port group named VLAN100, use the following:

New-NetworkAdapter -VM VM001 '
    -PortGroup (Get-VDPortgroup -Name VLAN100) -StartConnected

Name                 Type       NetworkName  MacAddress         WakeOnLan
                                                                  Enabled
----                 ----       -----------  ----------         ---------
Network adapter 2    c10e000e     VLAN100      00:50:56:be:0f:7d      False

Though no longer common due to the rise of virtualization, it is still possible for an application license to be based on the Media Access Control (MAC) address of the server’s network card. In such a case, it is possible to assign a static MAC address to the network adapter. Using a static MAC address prevents the MAC address from being changed during the application’s life cycle. Whatever the reason for adding a static MAC address to a VM, setting a static MAC address is accomplished with the -MacAddress parameter. Remember that the specified MAC address must be in the valid VMware range for static MAC addresses of 00:50:56:00:00:00–00:50:56:3F:FF:FF. Use the following to create a new network adapter with a static MAC address:

New-NetworkAdapter -VM VM001 '
    -PortGroup (Get-VirtualPortGroup -Standard -Name Public -VMHost ESX01) '
    -MacAddress 00:50:56:03:15:10

Once the new network adapter has been added, it is possible to change its settings using the Set-NetworkAdapter cmdlet. Perhaps there is a need to change the MAC address to the static MAC address used to create the production license for your application. Use the code that follows; just substitute your static address for the one used in the -MacAddress parameter:

Get-VM VM001 | Get-NetworkAdapter -Name "Network adapter 2" | 
    Set-NetworkAdapter -MacAddress 00:50:56:03:15:10 -Confirm:$false

As a security best practice, it’s recommended that all unused hardware be removed from your systems. (You can learn more about hardening your VM in Chapter 13, “Hardening the vSphere Environment.”) Suppose your backup application has been upgraded and now uses storage array–based snapshots to create full-image backups. The extra network adapters connected to the now redundant backup network will no longer be used and need to be removed. Removing an adapter is done using the Remove-NetworkAdapter cmdlet. This cmdlet accepts only one parameter: -NetworkAdapter. The parameter specifies the NetworkAdapter object to be removed. You can retrieve NetworkAdapter objects using the Get-NetworkAdapter cmdlet.

Remember, removing a network adapter from a powered-on virtual machine is supported only on virtual hardware version 7 or later—and then only if the guest OS supports hot removal. Be sure to power off the VM before running this code if you are working with an earlier virtual hardware version:

Get-VM VM001 | Get-NetworkAdapter | ?{$_.NetworkName -eq '
    "BACKUPVLAN10"} | Remove-NetworkAdapter -Confirm:$false

Assigning a Network

Once a network adapter is created, there’s not much that can be changed. But there may come a time when you need to move your VM to another network. You can accomplish this with the Set-NetworkAdapter cmdlet by using the -PortGroup parameter. For example, to move VM001 from VM_VLAN101 to VM_VLAN202 use the following:

Get-VM VM001 | 
    Get-NetworkAdapter | 
    Where-Object NetworkName -eq VLAN100 | 
    Set-NetworkAdapter -confirm:$false '
       -Portgroup (Get-VirtualPortGroup -Name Public -Standard -VMHost ESX01)

That’s really simple, isn’t it? Well, switching networks probably also means the VM will need to be assigned a different IP address, subnet mask, and/or gateway. None of that was handled in the code that moved the VM from VLAN100 port group to the Public port group, so the virtual machine probably won’t have network connectivity anymore. To change the virtual machine’s IP address, you can use the Invoke-VMScript cmdlet. Listing 8-3 uses the new PowerShell cmdlets in Windows Server 2012 to change the IP address inside the guest operating system for VM001.

Listing 8-3: Changing guest IP address with Invoke-VMScript

$HostCredential = Get-Credential
$GuestCredential = Get-Credential
$ScriptText = 'Get-NetIPAddress | 
    Where-Object IPAddress -eq 192.168.2.37 |
    Remove-NetIPAddress -Confirm:$false -PassThru |
    New-NetIPAddress -IPAddress 10.10.2.5 '
        -PrefixLength 22 '
        -DefaultGateway 10.10.2.1'

Invoke-VMScript -HostCredential $hostCreds '
    -GuestCredential $GuestCredential '
    -ScriptText $ScriptText '
    -VM VM001

Adding a Virtual Disk

If there is one universal truth, it is that there is no such thing as enough storage. No matter how much disk space is assigned to a VM, it will eventually need more. As a best practice, always try to right-size VM disks from the start and determine a reasonable amount of free space. There is no need to wastefully overallocate—more can be provisioned online. However, reducing allocated storage is not a trivial task.

To add a new hard disk to a VM, use the New-HardDisk cmdlet. The -CapacityGB parameter allows you to specify the size of the new virtual disk.

Specify the virtual machine to which the new virtual disk will be added by using the -VM parameter, or pass the virtual machine object (retrieved using the Get-VM cmdlet) on the pipeline, as demonstrated in the next example. To create a new virtual disk with a size of 10 GB and assign it to VM001, use the following:

Get-VM VM001 | New-HardDisk -CapacityGB 10

CapacityGB Persistence                                Filename
---------- -----------                                --------
10.000     Persistent          [Datastore1] VM001/VM001_1.vmdk

In some cases, it will be necessary to store the new hard disk on a different datastore. To specify a datastore that is different from the default (the datastore where the virtual machine’s configuration file [VMX] is stored), use the -Datastore parameter:

New-HardDisk -VM VM001 -CapacityGB 10 -Datastore Datastore2

CapacityGB Persistence                                Filename
---------- -----------                                --------
10.000     Persistent            [Datastore2] VM001/VM001.vmdk

When configuring a new hard disk, it is also possible to specify how changes are written to the disk. Table 8-3 lists the options available.

Table 8-3: Virtual disk modes

ModeDescription
PersistentThe default mode and the only one that allows snapshots. Changes are immediately and permanently written to the disk.
IndependentPersistentThe disk is not affected by snapshots. Changes are immediately and permanently written to the disk.
IndependentNonPersistentChanges to this disk are discarded when you power off the VM or revert to a snapshot.

Use the -Persistence parameter to make the disk Persistent, IndependentPersistent, or IndependentNonPersistent. In addition to write behavior, the allocation format can be specified. For example, use the following to specify that the new disk be deployed using thin provisioning with the -ThinProvisioned switch:

Get-VM VM001 | New-HardDisk -CapacityGB 10 '
    -Persistence IndependentPersistent -StorageFormat Thin

Besides creating new virtual disks, the New-HardDisk cmdlet can be used to attach an existing virtual disk. The path to this disk must be specified using the -DiskPath parameter:

New-HardDisk -VM VM001 -DiskPath "[Datastore2] OtherVM/OtherVM.vmdk"

What about adding a raw device mapping (RDM)? No problem. Simply specify the disk type using the -DiskType parameter and pass the disk’s location (or console device name) to the -DeviceName parameter. Valid disk types for RDM disks are rawVirtual or rawPhysical. The disk’s location can be retrieved using the Get-ScsiLun cmdlet. Here’s the code and a typical return:

Get-VMHost ESX01 | Get-ScsiLun -LunType disk | Select ConsoleDeviceName

ConsoleDeviceName
-----------------
/vmfs/devices/disks/naa.6006048c8d27a37983d99994cfddcea5
/vmfs/devices/disks/naa.6006048c877401f6aaa26f591bf602d6
/vmfs/devices/disks/mpx.vmhba1:C0:T0:L0

Assuming that the disk with identifier naa.6006048c8d27a37983d99994cfddcea5 is the RDM disk that needs to be assigned to the VM, the following code would attach that base logical unit number (LUN) as an RDM:

New-HardDisk -VM VM001 -DiskType rawVirtual -DeviceName '
    /vmfs/devices/disks/naa.6006048c8d27a37983d99994cfddcea5

CapacityGB Persistence                                Filename
---------- -----------                                --------
50.000     Persistent          [Datastore1] VM001/VM001_1.vmdk

Removing a Virtual Disk

Sometimes it may be necessary to remove a disk from a VM. Removal is accomplished with the Remove-HardDisk cmdlet. This cmdlet requires only one parameter: -HardDisk. The -HardDisk parameter specifies the hard disk(s) to be removed.

To remove all hard disks from a VM named VM001, use the following:

Get-VM VM001 | Get-HardDisk | Remove-HardDisk -Confirm:$false

This may not be very useful. Chances are a specific disk needs to be removed. For example, to remove all RDM disks from a virtual machine, use the following:

Get-HardDisk -VM VM001 -DiskType rawVirtual,rawPhysical | '
    Remove-HardDisk -Confirm:$false

To remove just one specific disk, specify the details to filter out that disk using a unique property like Name, Id, or Filename. Assuming a VM hard disk named Hard Disk 2 needed to be removed from virtual machine VM001:

Get-HardDisk -VM VM001 -Name "Hard disk 2" | Remove-HardDisk -Confirm:$false

Notice that the Remove-HardDisk cmdlet only removes the specified hard disk from the virtual machine. It doesn’t delete the actual virtual disk file from the datastore. To permanently delete the files from the datastore itself and not simply remove the hard disk from the virtual machine, add the -DeletePermanently switch:

Get-HardDisk -VM VM001 -Name "Hard disk 19" | '
    Remove-HardDisk -Confirm:$false -DeletePermanently

The hardest part of removing a virtual hard disk can be finding the right virtual hard disk. It starts simply enough. For a virtual machine that uses only two virtual disks, chances are it’s straightforward. But what about removing a disk from a virtual machine running multiple Microsoft SQL Server instances that happens to have 26 disks?

To make matters worse, the SQL administrator probably said, “Remove the K: drive” or “Remove Windows disk 21.”

What then? How does one determine which virtual disk to remove? The same problem arises when extending disks, but first things first.

Listing 8-4 contains a function to assist in identifying the right virtual disk. The Get-VMDiskMapping function can be used on hardware level 4 and up. Using mixed SCSI adapter types is not supported on hardware level 4, however.

Listing 8-4: Determining which virtual disk corresponds to which Windows disk

function Get-VMDiskMapping {
<#
.SYNOPSIS
  Creates a report to match Windows disk numbers and their
  virtual disk counterparts.
.DESCRIPTION
  This function creates an overview of the virtual machine's
  virtual disks and their Windows counterparts.
.NOTES
  Source:  Automating vSphere Administration
  Authors: Luc Dekens, Arnim van Lieshout, Jonathan Medd,
            Alan Renouf, Glenn Sizemore, Brian Graf,
            Andrew Sullivan.PARAMETER VM
  Specify the virtual machine to report on.
.PARAMETER HostCredential
  Specify a PSCredential object containing the credentials you
  want to use for authenticating with the host.
.PARAMETER GuestCredential
  Specify a PSCredential object containing the credentials you
  want to use for authenticating with the VM guest OS.
.EXAMPLE
  PS> Get-VM VM001 | Get-VMDiskMapping
.EXAMPLE
  PS> Get-VM VM001 | Get-VMDiskMapping -hostCredential $hostCred 
-guestCredential $guestCred | Out-GridView
#>
  Param (
   [parameter(ValueFromPipeline = $true, Mandatory = $true,
   HelpMessage = "Enter a vm entity")]
     [VMware.VimAutomation.ViCore.Impl.V1.Inventory.VirtualMachineImpl]$VM,
   [parameter(Mandatory = $false,
   HelpMessage = "Enter a PSCredential object for the host")]
    [System.Management.Automation.PSCredential]$hostCredential,
   [parameter(Mandatory = $true,
   HelpMessage = "Enter a PSCredential object for the guest")]
    [System.Management.Automation.PSCredential]$guestCredential)

#Create vbs scriptfile
  $FileName = [System.IO.Path]::GetTempFileName()
'Set objReg = GetObject("winmgmts:{impersonationLevel=
impersonate}!\.
ootdefault:StdRegProv")' > $filename
'Set objWMI = GetObject("winmgmts:{impersonationLevel=
impersonate}!\.
ootcimv2")' >> $filename
'Set colPCISlotNumber = CreateObject("Scripting.Dictionary")'
 >> $filename
'objReg.EnumKey &H80000002,"SYSTEMCurrentControlSetEnum
PCI", colHardwareId' >> $filename
'For Each HardwareId In colHardwareId' >> $filename
'  objReg.EnumKey &H80000002,"SYSTEMCurrentControlSetEnum
PCI" & HardwareId, colControllerId' >> $filename
'  For Each ControllerId In colControllerId' >> $filename
'    objReg.GetDWORDValue &H80000002,"SYSTEM
CurrentControlSetEnumPCI" & HardwareId & "" & 
ControllerId, "UINumber", dwUINumber' >> $filename
'    colPCISlotNumber.Add "PCI" & UCase(HardwareId) & "" & 
UCase(ControllerId), dwUINumber' >> $filename
'  Next' >> $filename
'Next' >> $filename
'Set colDiskDrive = objWMI.ExecQuery("Select * from Win32_
DiskDrive")' >> $filename
'Set colSCSIControllerDevice = objWMI.ExecQuery("Select * 
from Win32_SCSIControllerDevice")' >> $filename
'WScript.Echo "DiskPNPDeviceId,Index,SCSIPort,SCSITargetId,
Size,CtrlPNPDeviceId,CtrlPCISlotNumber"' >> $filename
'For Each Disk in colDiskDrive' >> $filename
'  For Each item in colSCSIControllerDevice' >> $filename
'    If Replace(Split(item.Dependent,chr(34))(1),"\","") = 
Disk.PNPDeviceId Then' >> $filename
'      CtrlPNPDeviceId = UCase(Replace(Split(item.Antecedent,
chr(34))(1),"\",""))' >> $filename
'      Exit For' >> $filename
'    End If' >> $filename
'  Next' >> $filename
'  WScript.Echo Disk.PNPDeviceId & "," & Disk.Index & "," & 
Disk.SCSIPort & "," & Disk.SCSITargetId & "," & Disk.Size & 
"," & CtrlPNPDeviceId & "," & colPCISlotNumber.Item(
CtrlPNPDeviceId)' >> $filename
'Next' >> $filename
#Determine location to copy script to
  $temp = Invoke-VMScript "echo %temp%" -vm $VM '
      -HostCredential $hostCredential -GuestCredential '
      $guestCredential -ScriptType "bat"
  $destFileName = $temp.Trim("'r'n") + "guestScsiInfo.vbs"
  Copy-VMGuestFile -Source $FileName -Destination '
      $destFileName -VM $VM -LocalToGuest -HostCredential '
      $hostCredential -GuestCredential $guestCredential
  Remove-Item $FileName
#Get Windows disk info
  $error.Clear()
  $Out = (Invoke-VMScript '
      "cscript /nologo $destFileName && del $destFileName" '
      -vm $VM -HostCredential $hostCredential '
      -GuestCredential $guestCredential '
      -ScriptType "bat").ScriptOutput
  
if (!$error -and $Out) 
  {
    $WinDisks = $Out | ConvertFrom-Csv
    #Determine SCSIPort offset
    $portOffset = ($WinDisks | Where-Object {$_.SCSIPort} | '
        Measure-Object -Property SCSIPort -Minimum).Minimum
    #All entries that don't match any known pciSlotNumber are
    #attached to scsi0. Change these entries to the pciSlotnumber
    #of scsi0
    $scsi0pciSlotNumber = ($VM.Extensiondata.Config.ExtraConfig | 
      Where-Object{$_.key -like "scsi0.pciSlotNumber"}).value
    $scsiPciSlotNumbers= @()
    $VM.Extensiondata.Config.ExtraConfig | 
      Where-Object {$_.key -like "scsi?.pciSlotNumber"} | 
      ForEach-Object{
        $scsiPciSlotNumbers += $_.value
      }
    $WinDisks | Foreach-Object {
        if ($scsiPciSlotNumbers -notcontains $_.CtrlPCISlotNumber) 
        {
            $_.CtrlPCISlotNumber = ($VM.ExtensionData.Config.Extraconfig | 
              Where-Object{$_.key -like "scsi0.pciSlotNumber"}).value
        }
    }
    #Create DiskMapping table
    foreach ($VirtualSCSIController in ($VM.Extensiondata.Config.Hardware.Device 
      | Where-Object {$_.DeviceInfo.Label -match "SCSI Controller"})) 
    {
      foreach ($VirtualDiskDevice in ($VM.Extensiondata.Config.Hardware.Device | 
         Where-Object {$_.ControllerKey -eq $VirtualSCSIController.Key})) 
      {
        $VirtualDisk = New-Object PSObject -Property @{ 
          VMSCSIController = $VirtualSCSIController.DeviceInfo.Label
          VMDiskName = $VirtualDiskDevice.DeviceInfo.Label
          SCSI_Id = "{0} : {1}" -f $VirtualSCSIController.BusNumber,
              $VirtualDiskDevice.UnitNumber
          VMDiskFile = $VirtualDiskDevice.Backing.FileName
          VMDiskSizeGB = $VirtualDiskDevice.CapacityInKB * 1KB / 1GB
          RawDeviceName = $VirtualDiskDevice.Backing.DeviceName
          LunUuid = $VirtualDiskDevice.Backing.LunUuid
          WindowsDisk = ""
          WindowsDiskSizeGB = 0
        }
        #Match disks
        if ([int]$vm.version.ToString().Replace("v","") -lt 7)
        {
            # For hardware v4 match disks based on controller's SCSIPort an    
            # disk's SCSITargetId.
            # Not supported with mixed scsi adapter types.
            $DiskMatch = $WinDisks | Where-Object {($_.SCSIPort – $portOffset) '
              -eq $VirtualSCSIController.BusNumber -and '
              $_.SCSITargetID -eq $VirtualDiskDevice.UnitNumber}
        }
        else 
        {
            # For hardware v7+ match disks based on controller's pciSlotNumber 
            # and disk's SCSITargetId
            $DiskMatch = $WinDisks | Where-Object {$_.CtrlPCISlotNumber -eq '
    ($VM.Extensiondata.Config.Extraconfig | Where-Object {$_.key -match '
    "scsi$($VirtualSCSIController.BusNumber).pcislotnumber"}).value '
    -and $_.SCSITargetID -eq $VirtualDiskDevice.UnitNumber}        }
        if ($DiskMatch)
        {
          $VirtualDisk.WindowsDisk = "Disk $($DiskMatch.Index)"
          $VirtualDisk.WindowsDiskSizeGB = $DiskMatch.Size / 1GB
        }
        else 
        {
            Write-Warning "No matching Windows disk found for SCSI id ↵
$($virtualDisk.SCSI_Id)"
        }
        $VirtualDisk
      }
    }
  }
  else 
  {
    Write-Error "Error Retrieving Windows disk info from guest"
  }
}

The output of the Get-VMDiskMapping function from Listing 8-4 is best viewed in table format using the Format-Table cmdlet, or even better, using the Out-GridView cmdlet. Figure 8-2 shows a sample output.

$map = Get-VM App04 | Get-VMDiskMapping -guestCredential $guestCred
$map | Out-GridView
c08f002.tif

Figure 8-2: Get-VMDiskMapping sample output

Extending a Virtual Disk

Given enough time, almost any VM will eventually run out of disk space. To extend that disk use the Set-HardDisk cmdlet. The -CapacityGB parameter specifies the new size of the virtual disk. For example, to extend virtual disk Hard disk 2 of virtual machine VM001 to 10 GB, use the following:

Get-VM VM001 | Get-HardDisk -Name "Hard disk 2" |
    Set-HardDisk -CapacityGB 10

Extending the virtual disk is straightforward and easy to achieve. But extending the virtual disk is only half the work. Chances are the disk was extended because the guest’s partition was reaching its capacity. Therefore, it is also necessary to extend the guest’s partition to use the newly added capacity. But don’t worry; the Set-HardDisk cmdlet also supports extending the guest’s partition.

To extend the guest’s partition, the Set-HardDisk cmdlet calls the Invoke-VMScript cmdlet in the background and uses a script that runs inside the VM in the context of the specified guest user to extend the guest’s partition. These scripts are located in the Scripts folder in the PowerCLI installation directory. To specify the guest’s partition, use the -Partition parameter. If a partition isn’t specified using this parameter, the last partition on the hard disk is expanded. It is possible to specify a Windows partition, by specifying the drive letter without the colon. There are some restrictions, however:

  • To expand a partition that isn’t the last partition on a multipartitioned disk in Windows, the disk must be dynamic.
  • On Linux, only the last partition can be expanded when the disk is multipartitioned and Logical Volume Manager (LVM) is not supported.

Extending the guest’s partition is currently only supported on Windows XP SP3 or newer, and ext3 partitions on Linux RedHat Enterprise 5.

To extend the Windows partition on the previously extended Hard disk 2 on virtual machine VM001 to 100GB and expand the guest partition, use the following:

Get-VM VM001 | Get-HardDisk -Name "Hard disk 2" | 
    Set-HardDisk -CapacityGB 100 '
       -HostCredential $hostCredential '
       -GuestCredential $guestCredential '
       -ResizeGuestPartition -Partition E

When the partition being expanded is the system disk, additional restrictions apply:

  • Linux system disks cannot be expanded.
  • Windows 2003 and Windows XP require a helper virtual machine to expand the system partition.

On Windows, the helper virtual machine can be specified using the -HelperVM parameter. When a helper virtual machine is used, all VMs associated with the disk and the helper virtual machine must be powered off before expanding the disk. When you resize more than one disk using a helper virtual machine, the disks are resized one at a time, causing the helper machine to power on and off for each virtual disk. This process might slow down the cmdlet’s performance.

For example, to expand the system disk on VM001, where the system partition happens to be Hard disk 1 and VM002 will be used as the helper virtual machine, both VM001 and VM002 need to be powered off before extending the disk. Notice that the guest logon credentials used in Listing 8-5 are the guest logon credentials for the helper virtual machine.

Listing 8-5: Automated system partition expansion

Get-VM VM001,VM002 | Shutdown-VMGuest -Confirm:$false

While ((Get-VM VM001).PowerState -eq "PoweredOn"){
  Sleep -Seconds 2
}
While ((Get-VM VM002).PowerState -eq "PoweredOn"){
  Sleep -Seconds 2
}

Get-VM VM001 | Get-HardDisk -Name "Hard disk 1"} | '
    Set-HardDisk -CapacityGB 60 '
       -HostCredential $hostCredential '
       -GuestCredential $guestCredential '
       -HelperVM (Get-VM VM002)

Get-VM VM001,VM002 | Start-VM -Confirm:$false

Sit back, relax, and enjoy the show. This is really cool stuff!

All the steps involved in expanding the system partition using a helper virtual machine (normally done by hand) are now fully automated. The following tasks are performed automatically for you:

  • Extend the virtual disk in VM001.
  • Add the virtual disk to the helper virtual machine VM002.
  • Power on the helper virtual machine VM002.
  • Expand the partition on the helper virtual machine VM002.
  • Remove the virtual disk from the helper virtual machine VM002.
  • Shut down the helper virtual machine VM002.

Changing Other Hardware

When it’s time to install software from a CD or ISO image, a CD drive will need to be added to the VM if it doesn’t have one already. A new CD drive can be added to your virtual machine using the New-CDDrive cmdlet. You can connect the CD drive to an ISO image file:

 Get-VM VM001 | New-CDDrive -IsoPath '
    "[Datastore01] ISO/gparted-live.iso" -StartConnected

or direct to the host’s CD drive:

Get-VM VM001 | New-CDDrive -HostDevice '
    "/vmfs/devices/cdrom/mpx.vmhba32:C0:T0:L0"

For security purposes, it is a good practice to remove all virtual hardware from your VM that is not used. For example, VMs rarely contain a floppy drive anymore, as it’s very unlikely that one will ever be needed. But there may be a need for a temporary floppy drive for a legacy application. To connect a floppy drive, use the New-FloppyDrive cmdlet. To connect a floppy image file to our VM001, use the following:

Get-VM VM001 | New-FloppyDrive -FloppyImagePath '
    "[Datastore01] ISO/floppy.flp"

A new floppy image can be created by using the -NewFloppyImagePath parameter and specifying a datastore path to the new floppy image:

Get-VM VM001 | New-FloppyDrive -NewFloppyImagePath '
    "[Datastore01] ISO/myNewFloppy.flp"

Beginning with vSphere 4, it became possible to add VMDirectPath I/O pass-through devices, also referred to as PCI pass-through devices. The other type of pass-through device available is the SCSI pass-through device. You can connect up to six pass-through devices to a virtual machine by using the Add-PassthroughDevice cmdlet. Remember that before PowerCLI can connect a PCI pass-through device to your virtual machine, the device must be enabled for pass-through on the ESX host first:

$deviceList = Get-VMHost ESX01 | Get-PassthroughDevice
Get-VM VM001 | Add-PassthroughDevice '
    -PassthroughDevice $deviceList[0]

Optimize Storage Usage with Thin Provisioning

When a new virtual disk is created and disk type is not specified, the disk type is thick by default for any VMFS datastore. A thick virtual disk preallocates all the space during the creation of the disk. You can also create a thin virtual disk. A thin virtual disk does not preallocate the space. The blocks of a thin virtual disk are allocated on demand when first written to. You create a thin-provisioned disk by specifying the -StorageFormat Thin parameter on the New-HardDisk cmdlet:

Get-VM VM001 | New-HardDisk -CapacityGB 10 -StorageFormat Thin

In a VMware environment, every VM needs to have its own boot and data volumes. The initial size of the boot volume is dictated by the size of the operating system and applications. The size of the data volume is dictated by the size of the existing application data. Typically, there’s also an amount of free space added to each partition to accommodate future patches, service packs, upgrades, and application data growth. Traditionally data volumes are designed to take into account additional data for the upcoming year or even years, because disks used to be fixed in an era when servers were installed on direct attached disks. Nowadays, servers store their files on storage arrays, which are more flexible in size, and presented disks (or logical units) can easily be extended.

But still, servers are many, many times designed the old-school way. This leads to lots of free unutilized space on your expensive storage arrays. Using thin provisioning, you can reclaim this wasted space and increase your storage capacity utilization. This boost in capacity utilization (typically 20–30 percent) also saves an equal percentage of your annual storage costs, because the same servers and applications are stored on less space.

Unused space can be reclaimed by converting the virtual disk to thin. You can accomplish this by using vMotion or by converting the disk in place.

Converting a Virtual Disk Using Storage vMotion

Converting a thick virtual disk to a thin-provisioned virtual disk, or vice versa, can be accomplished by moving the disk to another datastore using storage vMotion. Because storage vMotion creates a new copy of the hard disk on the destination volume, it can specify a different disk type for the copy on the destination volume. This is accomplished using the Move-HardDisk cmdlet and specifying the new datastore with the -Datastore parameter and the new disk type with the -DiskType parameter. The following code uses Storage vMotion to move all virtual disks on virtual machine VM001 to datastore Datastore02 and converts them to thin disks on the fly:

Get-VM VM001 | Get-HardDisk | Move-HardDisk '
    -Datastore Datastore02 -StorageFormat Thin

To convert a single hard disk on a VM, simply specify the -Name parameter. The following code uses Storage vMotion to move only Hard disk 2 on virtual machine VM001 and converts it to thin provisioning on the fly:

Get-VM VM001 | Get-HardDisk -Name "Hard disk 2" | '
    Move-HardDisk –Datastore Datastore02 -StorageFormat Thin

Converting a Virtual Disk in Place

If faced with only one datastore and unable to utilize storage vMotion, you can convert a virtual disk in place. Do so by copying the virtual disk and then reconfiguring the virtual machine to use the new copy. This method has some drawbacks:

  • Because the workflow actually replaces the virtual disk with a cloned copy, the virtual machine must be powered off.
  • There must be enough free space on your datastore to store the copy of the virtual disk.
  • For the Copy-HardDisk cmdlet to work, the disk must be connected directly to an ESXi host.

Listing 8-6 contains a function that creates a thin-provisioned cloned copy of your thick virtual disk.

Listing 8-6: Converting thick to thin in place using a disk copy

function Set-ThinDisk {
  Param (
    [parameter(ValueFromPipeline = $true, Mandatory = $true)]
    [VMware.VimAutomation.ViCore.Types.V1.VirtualDevice.HardDisk]$hardDisk
  ,
    [Parameter(Mandatory = $true, ParameterSetName = "cred")]
    [System.Management.Automation.PSCredential]$credential
  ,
    [Parameter(ParameterSetName = "user")]
    [ValidateNotNullOrEmpty()]
    [string]$user = "root"
  ,
    [Parameter(Mandatory = $true, ParameterSetName = "user")]
    [string]$password
  ,
    [switch]$replace
  )
  process {
    if ($hardDisk.Parent.PowerState -eq "PoweredOff") {
      if ($hardDisk.StorageFormat -ne "Thin") {
        if ($credential) {
          $esxHost = Connect-VIServer '
              -Server $hardDisk.Parent.host.name '
              -Credential $credential -NotDefault
        }
        else {
          $esxHost = Connect-VIServer -Server '
              $hardDisk.Parent.host.name -User $user '
              -Password $password -NotDefault
        }
        $thinFile = $hardDisk.Filename.Replace("/","/thin_")
        $datastore = '
            $hardDisk.Filename.split('[')[1].split(']')[0]
        $esxHardDisk = Get-HardDisk -server $esxHost '
            -Datastore $datastore '
            -DatastorePath $hardDisk.Filename
        Copy-HardDisk -HardDisk $esxHardDisk '
            -DestinationPath $thinFile '
            -DestinationStorageFormat "thin" | Out-Null
        Disconnect-VIServer $esxHost -Confirm:$false

        $spec = New-Object VMware.Vim.VirtualMachineConfigSpec
        $spec.deviceChange = New-Object '
            VMware.Vim.VirtualDeviceConfigSpec[] (2)
        $spec.deviceChange[0] = New-Object '
            VMware.Vim.VirtualDeviceConfigSpec
        $spec.deviceChange[0].operation = "remove"
        if ($replace) {
          $spec.deviceChange[0].fileOperation = "destroy"
        }
        $spec.deviceChange[0].device = $hardDisk.ExtensionData

        $spec.deviceChange[1] = New-Object '
            VMware.Vim.VirtualDeviceConfigSpec
        $spec.deviceChange[1].operation = "add"
        $spec.deviceChange[1].device = New-Object '
            VMware.Vim.VirtualDisk
        $spec.deviceChange[1].device.key = -100
        $spec.deviceChange[1].device.backing = New-Object '
            VMware.Vim.VirtualDiskFlatVer2BackingInfo
        $spec.deviceChange[1].device.backing.fileName = '
            $thinFile
        $spec.deviceChange[1].device.backing.diskMode = '
            "persistent"
        $spec.deviceChange[1].device.backing.thinProvisioned = '
            $true
        $spec.deviceChange[1].device.controllerKey = '
            $hardDisk.ExtensionData.ControllerKey
        $spec.deviceChange[1].device.unitNumber = '
            $hardDisk.ExtensionData.UnitNumber

        $vm = Get-View -Id $hardDisk.ParentID -Property Name
        $vm.ReconfigVM_Task($spec) | Out-Null
      }
      else {
        Write-Error "Virtual disk already thin provisioned"
      }
    }
    else {
      Write-Error "Virtual machine must be powered off"
    }
  }
}
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset