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 😉