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.
sdrs-disable
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.