Teaching an Optimize and Scale class this week I was asked if there is chance to automatically change the virtual hardware once a VM has been shut down or powered off.

Scenario – Customer wants to change the CPU / RAM of their XenDesktops with minimal impact on the end-user. If a VM is powered off vCenter should change the hardware and wait until another components powers the VM on again.

At first I was not sure how this can be done. Using PowerCLI for changing the virtual hardware? – easy… but how can we call this script once the VM is powered off? After a minute I remebered that we can use vCenter alarms to trigger more than just snmp/email actions if a specific Event/Condition occurs. We can also run cmds and therefore call a PowerCLI script.

Requirements:

Using AD-based authentication for the PowerCLI script makes it easier regarding the Authentication mechanism. Therefore the vCenter server must run with Active Directory Service Account that has the proper permissions on the vCenter level

vcenteralarmaction01

vcenteralarmaction02

Chapter 1:  Easy going – Hardcode the CPU count in the script

Create the PowerCLI script

Now we create the modify-cpu.ps1 script that will be stored in the C:\scripts\

with the following content. (Note: the CPU count must be hardcoded in the script by changing the $numCPU parameter. Be aware of that this script changes the count of Cores and stays with 1 virtual socket.

Complete Script – modify-cpu.ps1:

param(
[string]$vmname
)

$vCenter = 'localhost'

$numCPU = 4
####
# Include VMware-PowerCLI SnapIn
if(!(Get-PSSnapin | Where {$_.name -eq "vmware.vimautomation.core"}))
{
    try
    {
        Write-Host "Adding PowerCLI snap in"
        Add-PSSnapin VMware.VimAutomation.Core -ea 0| out-null
    }
    catch
    {
        throw "Could not load PowerCLI snapin"
    }
}

Connect-VIServer $vCenter

$vm = get-vm $vmname
$spec=New-Object –Type VMware.Vim.VirtualMAchineConfigSpec –Property @{"NumCoresPerSocket" = $numCPU;"numCPUs" = $numCPU}
$vm.ExtensionData.ReconfigVM_Task($spec) | out-null

Disconnect-VIServer $vCenter -confirm:$false

 

Create the vCenter alarm that will call the script above with the VM name as parameter

Select the VM you want the virtual CPU count be changed after a shutdown and create a vCenter alarm (wow I am really using the webclient)

Give it a valuable name and describiton and select to monitor for a specific event

vcenteralarmaction03

As an event where the alarm and therefore the action will be triggered we select VM – Powered off

vcenteralarmaction04

And finally as an action we call our created PowerCLI script in C:\scripts with the following statement:

“c:\windows\system32\cmd.exe” “/c echo.|powershell.exe -nologo -noprofile -noninteractive C:\scripts\modify-cpu.ps1 {targetName}”

With the {targetName} variable we can transfer the name of the virtual machine that caused the Trigger.

vcenteralarmaction05

And voila. If the VM is now getting powered-off -> the change of the virtual hardware will be done.

vcenteralarmaction06

(German language, hm!? Doesn’t sound it quiet nice :P)

vcenteralarmaction07

If the configuration is not working as expected, validate the functionality by call cmd with the user that is running the vCenter service:

> Open CMD

> runas /user:Domain\vCenteruser cmd

> “c:\windows\system32\cmd.exe” “/c echo.|powershell.exe -nologo -noprofile -noninteractive C:\scripts\modify-hardware.ps1 {targetName}”

Chapter 2:  Make it more awesome by using VMs and Templates Folders

To be more flexible on the hardware configuration I wanted to have the following functionality:

Drag and Drop a VM in a specific folder. If the VM is powered off change the virtual hardware based on specific settings. The settings should be extracted from the folder name.

Foldername specification: modify_ressourcetype_value

for example I create 4 Folders:

  • modify_cpu_2
  • modify_cpu_4
  • modify_ram_4
  • modify_ram_8

If you drag a VM in the modify_ram_8 folder and power it off, the VM will be configured with 8GB memory.

So I needed to change my script in a way that it gathers the folder the of the VM that is transmitted with the script call:

Complete Script – modify-hardware.ps1:

$vm = get-vm $vmname
$vmFolderName = $vm.Folder.Name

and split the foldername at the ‘_’ chars:

$items = $vmFolderName.Split('_')
$ressourceType = $items[1]
$amount = $items[2]

Now I can select the change RAM or CPU logic with a ‘switch’ statement:

switch($ressourceType){

    'cpu'
     {
         $vCPu= $amount
         $spec=New-Object –Type VMware.Vim.VirtualMAchineConfigSpec –Property @{"NumCoresPerSocket" =          
         $vCPu;"numCPUs" = $vCPu}
         $vm.ExtensionData.ReconfigVM_Task($spec) | out-null
      }
    'ram'
     {
          $NmbrRam = $amount
          set-vm -VM $vm -memoryGb $NmbrRam -confirm:$false
     } 
     default
     {
         write-verbose 'Wrong folder name specification'
     }
}

If the VM is not in a folder that matches the specification. Nothing will happen.

Transfer the following modify-hardware.ps1 script into C:\scripts\ and configure this time the alarms in the same way as described above – but now on the specific folder we have created for this solution.

The action call this time needs to be renamed since I created another script:

“c:\windows\system32\cmd.exe” “/c echo.|powershell.exe -nologo -noprofile -noninteractive C:\scripts\modify-hardware.ps1 {targetName}”

vcenteralarmaction08

Complete Script – modify-hardware.ps1:

param(
[string]$vmname
)

$vCenter = 'localhost'

###

# Include VMware-PowerCLI SnapIn
if(!(Get-PSSnapin | Where {$_.name -eq "vmware.vimautomation.core"}))
{
    try
    {
        Write-Host "Adding PowerCLI snap in"
        Add-PSSnapin VMware.VimAutomation.Core -ea 0| out-null
    }
    catch
    {
        throw "Could not load PowerCLI snapin"
    }
}

Connect-VIServer $vCenter

#Gather Objects and Data
$vm = get-vm $vmname
$vmFolderName = $vm.Folder.Name

#Split the folder name to extract parameter - Foldername must be modify_ressource_x
#while ressource must be ram or cpu and X the number of CPU or amount of RAM in GB

$items = $vmFolderName.Split('_')
$ressourceType = $items[1]
$amount = $items[2]
$items

switch($ressourceType){

    'cpu'
     {
         $vCPu= $amount
         $spec=New-Object –Type VMware.Vim.VirtualMAchineConfigSpec –Property @{"NumCoresPerSocket" =          
         $vCPu;"numCPUs" = $vCPu}
         $vm.ExtensionData.ReconfigVM_Task($spec) | out-null
      }
    'ram'
     {
          $NmbrRam = $amount
          set-vm -VM $vm -memoryGb $NmbrRam -confirm:$false
     } 
     default
     {
         write-verbose 'Wrong folder name specification'
     }
}

Disconnect-ViServer $vCenter -confirm:$false

Maybe some of you will benefit from this little script-collection. Have fun with it 😉