vSphere Cluster Host-VM rule affinity with PowerCLI

A colleague wanted a script that could make a Host-VM affinity rule so that certain VM´s that resided on one storage at one site would run on the hosts on that site, the cluster spanns over two sites so the VM´s can run at both sites but one datastore is primary at the first site and the other at the second site.  So He wanted to get the VM´s that have their storage on datastoreLocation1 to be in an host-vm affinity rule “Should Run” on the hosts that is in that site. In this community post I found most of the code, I made som adjustments but mostly the cred goes to @lucd22 and GotMoo.

Here you can see the screendump from the cluster settings

Here is the powerCLI that has been run

One thing here was also that this VMgroup should be updated once a day if there was new VM´s that needed to be included. So this lines should be run in a scheduled task, this will make the DRSVMGroup VMLocationDC1 to be updated with the VM´s currently located at that datastore:

$VM = get-datastore delad02 | get-vm
Update-DrsVMGroup -cluster vmw* -VMs $VM -groupVMName VMLocationDC1

Here are the PowerCLI functions for doing this. As you can see I have an switch parameter for -MustRun, that will create the rule as a Must Run on these hosts.

function New-DRSGroupRule{
<#
.SYNOPSIS
Create a new DRSGroupRule for VMs to reside on some hosts in a cluster

.DESCRIPTION
Use this function to create vms in a group and hosts in a group and a host-vm affinity

.PARAMETER  MustRun
A switch that will create the rule with Must Run on these host, if not set it will create the rule with should run.

.NOTES
Author: Niklas Akerlund / RTS (most of the code came from http://communities.vmware.com/message/1667279 @LucD22 and GotMoo)
Date: 2012-06-28
#>
	param (
	[Parameter(Position=0,Mandatory=$true,HelpMessage="A Cluster",
	ValueFromPipeline=$True)]
	$cluster,
	$VMHosts,
	$VMs,
	[string]$Name,
	[switch]$MustRun
	)
	
	$cluster = Get-Cluster $cluster

	$spec = New-Object VMware.Vim.ClusterConfigSpecEx
	$groupVM = New-Object VMware.Vim.ClusterGroupSpec 
	$groupVM.operation = "add" 
	$groupVM.Info = New-Object VMware.Vim.ClusterVmGroup
	$groupVM.Info.Name = "VM$Name"

	Get-VM $VMs | %{
	$groupVM.Info.VM += $_.Extensiondata.MoRef
	}
	$spec.GroupSpec += $groupVM

	$groupESX = New-Object VMware.Vim.ClusterGroupSpec 
	$groupESX.operation = "add"
	$groupESX.Info = New-Object VMware.Vim.ClusterHostGroup
	$groupESX.Info.Name = "Host$Name"

	Get-VMHost $VMHosts | %{
	$groupESX.Info.Host += $_.Extensiondata.MoRef
	}
	$spec.GroupSpec += $groupESX

	$rule = New-Object VMware.Vim.ClusterRuleSpec
	$rule.operation = "add"
	$rule.info = New-Object VMware.Vim.ClusterVmHostRuleInfo
	$rule.info.enabled = $true
	$rule.info.name = $Name
	if($MustRun){
		$rule.info.mandatory = $true
	}else{
		$rule.info.mandatory = $false
	}
	$rule.info.vmGroupName = "VM$Name"
	$rule.info.affineHostGroupName = "Host$Name"
	$spec.RulesSpec += $rule

	$cluster.ExtensionData.ReconfigureComputeResource($spec,$true)
}

function Update-DrsVMGroup {
<#
.SYNOPSIS
Update DRS VM group with a new collection of VM´s

.DESCRIPTION
Use this function to update the ClusterVMgroup with VMs that are sent in by parameters

.PARAMETER  xyz 

.NOTES
Author: Niklas Akerlund / RTS (most of the code came from http://communities.vmware.com/message/1667279 @LucD22 and GotMoo)
Date: 2012-06-28
#>
	param (
	$cluster,
	$VMs,
	$groupVMName)
	
    $cluster = Get-Cluster $cluster
    $spec = New-Object VMware.Vim.ClusterConfigSpecEx
    $groupVM = New-Object VMware.Vim.ClusterGroupSpec 
    #Operation edit will replace the contents of the GroupVMName with the new contents seleced below.
    $groupVM.operation = "edit" 

    $groupVM.Info = New-Object VMware.Vim.ClusterVmGroup
    $groupVM.Info.Name = $groupVMName 

    Get-VM $VMs | %{
        $groupVM.Info.VM += $_.Extensiondata.MoRef
    }
    $spec.GroupSpec += $groupVM

    #Apply the settings to the cluster
    $cluster.ExtensionData.ReconfigureComputeResource($spec,$true)
}

When I get the time I might add some more functionality to also remove rules and groups.

Comments

Magnus
Reply

great work Niklas, will try this immediately. thanks.

Stacy
Reply

Hi Niklas,

I’ve been reviewing the code from your site and VMware communities in order to create a PowerCLI script to update two DRS VM Groups on a stretched cluster nightly. I will be generating the new lists of VMs for the two DRS VM groups based on datastore names.
For example, the following commands below that I found appear to work for me as far as getting the latest lists of VMs for the two groups. I’m just not sure how to incorporate this into the code to update the two DRS groups. Also would like to have it output the latest lists to .csv files and have it email it. And perhaps down the road, add code to have it output to a log file. Any feedback would be appreciated, since I am very new at Powershell/PowerCLI.

Get-Cluster “clustername” | Get-vm | ?{($_.extensiondata.config.datastoreurl | %{$_.name}) -like “datastorex[1-3]_A*”} | Select-Object Name

Get-Cluster “clustername” | Get-vm | ?{($_.extensiondata.config.datastoreurl | %{$_.name}) -like “datastorez[4-6]_B*”} | Select-Object Name

philippe
Reply

Hello,

I’m not a PowerShell expert and I need your help.

I want to write a PS Script to compare where the VM actually running (ESX host) -> may be like “Get-VM | select name, VMhost” with the DRS hosts Group (list of ESX hosts) where she should run, if it doesn’t violate the DRS Host Affinity.

I don’t know how to collect the list of ESX Host (in DRS host group) which are associated with the VM DRS Group?

Thanks a lot for your help

Philippe

David
Reply

Your script works great. Thank you!

A question though… I have tried to modify your script from edit to add, but it does not work. Meaning, how can this be modified to append to the existing list instead of recreating each time?

I have numerous datastores and I am generating my VM list from DC1 and DC2 datastores.

ThomasD
Reply

Thats exactly what I was looking for! Works great! Cheers!