Storage DRS and PowerCLI Part 1: Get VM Overrides
Introduction
With Storage DRS (SDRS) enabled datastore clusters, we have many options to configure overrides on a per-VM basis. For example disabling SDRS for a specific VM or forcing SDRS to keep a VMs VMDK files on separate datastores (anti-affinity). However, one thing which has caught out our 2nd line team recently and caused us some SDRS performance issues is that when you manually Storage vMotion (svMotion) one or more VMDK files and pick a specific datastore as the destination, you have to tick a box that disables SDRS for the particular VM. This results in a VM Override being created for that VM. Consequently, SDRS will make no attempt to move these VMDKs when trying to rebalance the datastores in the datastore cluster.
To stop this being an issue I have now written a couple of PowerShell scripts using PowerCLI. The first one is to list the current VM Overrides. The second one is to bulk remove the VM Overrides that are not required. In this first post I will show you how to get a list of all VM Overrides for a specific datastore cluster. You can also see this information in the Web Client by going to the Storage view, selecting a datastore cluster, and going to Manage > Settings > VM Overrides.
The Process
The first thing to do is to retrieve the datastore cluster object using the Get-View
cmdlet (VMware refer to these as Storage Pods).
$StoragePod = Get-View -ViewType "StoragePod" -Filter @{"Name" = "TESTDATASTORECLUSTER"}
I am targeting a single datastore cluster, but you can target all clusters by omitting the -Filter
parameter.
If you now take a look at the $StoragePod.PodStorageDrsEntry.StorageDrsConfig.VmConfig
property, you can see a list of VMs and their current SDRS configuration as follows.
$StoragePod.PodStorageDrsEntry.StorageDrsConfig.VmConfig
Vm : VirtualMachine-vm-1535
Enabled :
Behavior :
IntraVmAffinity : False
IntraVmAntiAffinity :
VirtualDiskRules :
LinkedView :
Vm : VirtualMachine-vm-461
Enabled :
Behavior :
IntraVmAffinity : False
IntraVmAntiAffinity :
VirtualDiskRules :
LinkedView :
Vm : VirtualMachine-vm-1381
Enabled : False
Behavior :
IntraVmAffinity :
IntraVmAntiAffinity :
VirtualDiskRules :
LinkedView :
Vm : VirtualMachine-vm-1382
Enabled : False
Behavior :
IntraVmAffinity :
IntraVmAntiAffinity :
VirtualDiskRules :
LinkedView :
Vm : VirtualMachine-vm-1481
Enabled : False
Behavior :
IntraVmAffinity :
IntraVmAntiAffinity :
VirtualDiskRules :
LinkedView :
Vm : VirtualMachine-vm-1527
Enabled :
Behavior :
IntraVmAffinity :
IntraVmAntiAffinity :
VirtualDiskRules :
LinkedView :
....
As you can see, I have two VMs with IntraVmAffinity overrides configured and three VMs which have SDRS disabled.
You may also see VMs in this list with nothing configured, such as VirtualMachine-vm-1527
above. This threw me initially, as only a few random VMs without an override configured were showing up. I have since worked out in my lab that any VM which previously had a VM override but has since had it removed, will show up in this state. Meanwhile, any VM which has never had an override will not show in the list at all.
To filter out the VMs in the list that no longer have a VM Override configured, I need to check that the Enabled
and IntraVmAffinity
properties are not null.
$VMOverrides = $StoragePod.PodStorageDrsEntry.StorageDrsConfig.VmConfig | Where-Object {
-not (
($_.Enabled -eq $null) -and
($_.IntraVmAffinity -eq $null)
)
}
This will store all VMs with a current VM Override in the $VMOverrides
object. We can then loop over these and output the data as a PSCustomObject.
foreach ($Override in $VMOverrides) {
[PSCustomObject]@{
VirtualMachine = $Override.Vm
SDRSAutomationLevel = $Override.Enabled
KeepVMDKsTogether = $Override.IntraVmAffinity
}
}
VirtualMachine SDRSAutomationLevel KeepVMDKsTogether
-------------- ------------------- -----------------
VirtualMachine-vm-1481 False
VirtualMachine-vm-1527 False
VirtualMachine-vm-1528 False
VirtualMachine-vm-1547 False
VirtualMachine-vm-844 False
While this is great, it is returning the managed object reference (MoRef) for each VM. It would be much better if we could instead display the VM name. You can get the VM name from the MoRef using the Get-VM
cmdlet.
Get-VM -Id "VirtualMachine-vm-844"
Name PowerState Num CPUs MemoryGB
---- ---------- -------- --------
Test123 PoweredOn 1 3.000
Therefore, I am going to create a hashtable containing the MoRef for each VM as the keys, and the VM name as the values. We can then cross reference this lookup table later.
$VMLookup = @{}
foreach ($VM in (Get-VM -Id $VMOverrides.Vm)) {
$VMLookup.($VM.Id) = $VM.Name
}
To test the lookup table, we can call the hashtable and specify the MoRef as the key. This will return the name of the VM.
$VMLookup."VirtualMachine-vm-844"
Using this technique, we can now improve our foreach
loop from earlier to display the VM name.
foreach ($Override in $VMOverrides) {
[PSCustomObject]@{
VirtualMachine = $VMLookup."$($Override.Vm.Type)-$($Override.Vm.Value)"
SDRSAutomationLevel = $Override.Enabled
KeepVMDKsTogether = $Override.IntraVmAffinity
}
}
VirtualMachine SDRSAutomationLevel KeepVMDKsTogether
-------------- ------------------- -----------------
SL_Test False
RKTEST1 False
RKTEST0 False
ZERTOTEST1 False
Test123 False
We now have our list of current VM Overrides for this specific datastore cluster. All the above VMs have SDRS Automation set to false (disabling SDRS); and as there is no entry in the Keep VMDKs Together column, these VMs do not have an override for this setting and therefore will all be using the cluster default.
This output is OK, but when you view the VM Overrides view in the Web Client it is a bit more informative. For example rather than displaying just “true” or “false” for the SDRSAutomationLevel the Web Client will display “Fully Automated” or “Disabled”, and for KeepVMDKsTogether it will display “Yes” or “No”. Also, rather than displaying a blank when there is no override configured for a specific setting, the Web Client will instead display the cluster default, as this is the setting that will be taking effect. For completeness, I will show you have to replicate this behavior in our script.
To display the friendly name for the VM overrides we can use a switch
statement when building our PSCustomObject
.
foreach ($Override in $VMOverrides) {
[PSCustomObject]@{
VirtualMachine = $VMLookup."$($Override.Vm.Type)-$($Override.Vm.Value)"
SDRSAutomationLevel = Switch ($Override.Enabled) {
$true {"Fully Automated"}
$false {"Disabled"}
}
KeepVMDKsTogether = Switch ($Override.IntraVmAffinity) {
$true {"Yes"}
$false {"No"}
}
}
}
As you can see, we are now testing the value of both $Override.Enabled
and $Override.IntraVmAffinity
and depending on whether the value is $true
or $false
we are returning the required friendly name.
This takes care of the specific VM overrides but we will still see a number of blanks which are defaulting to the cluster default. To fix this we first need to assign the cluster default settings for both DefaultVmBehavior
and DefaultIntraVmAffinity
to separate variables. These settings can be retrieved from the $StoragePod
object that we created earlier.
Switch ($StoragePod.PodStorageDrsEntry.StorageDrsConfig.PodConfig.DefaultVmBehavior) {
"automated" {$DefaultVmBehavior = "Default (Fully Automated)"}
"manual" {$DefaultVmBehavior = "Default (No Automation (Manual Mode))"}
}
Switch ($StoragePod.PodStorageDrsEntry.StorageDrsConfig.PodConfig.DefaultIntraVmAffinity) {
$true {$DefaultIntraVmAffinity = "Default (Yes)"}
$false {$DefaultIntraVmAffinity = "Default (No)"}
}
Now we have two variables, $DefaultVmBehavior
and $DefaultIntraVmAffinity
, which contain a friendly name for the cluster default setting e.g. “manual” becomes “Default (No Automation (Manual Mode))” which is how it would display in the Web Client. With a simple addition to the two switch
statements we used creating our PSCustomObject
we can add a test for $null
which if true will return this default setting.
foreach ($Override in $VMOverrides) {
[PSCustomObject]@{
VirtualMachine = $VMLookup."$($Override.Vm.Type)-$($Override.Vm.Value)"
SDRSAutomationLevel = Switch ($Override.Enabled) {
$true {"Fully Automated"}
$false {"Disabled"}
$null {$DefaultVmBehavior}
}
KeepVMDKsTogether = Switch ($Override.IntraVmAffinity) {
$true {"Yes"}
$false {"No"}
$null {$DefaultIntraVmAffinity}
}
}
}
As you can see, now if either $Override.Enabled
or $Override.IntraVmAffinity
is $null
, instead of returning a blank we now return the cluster default variable. The whole script now displays VM overrides in a pretty nice format.
VirtualMachine SDRSAutomationLevel KeepVMDKsTogether
-------------- ------------------- -----------------
TEST Default (Fully Automated) No
RKTEST1 Disabled Default (Yes)
RKTEST0 Disabled Default (Yes)
Test123 Disabled Default (Yes)
TEST00008 Fully Automated Yes
Conclusion
I hope this has been helpful in showing you how to retrieve the VM Override information for a datastore cluster via PowerCLI. In part 2, I will focus on how to remove VM Overrides in bulk to ensure that Storage DRS performs as effectively as possible in your environment.