27 July 2011

Backup/Export Full DRS Rule Info via PowerShell

Updated 22 Jan 2015
Luc Dekens and I got together on a new PowerShell module for managing/exporting/importing DRS Rules and Groups. See our posts at http://www.lucd.info/2015/01/22/drsrule-drs-rules-and-groups-module and http://www.vnugglets.com/2015/01/drsrule-new-powershell-module-for-drs.html for all of the juicy details. Going forward, that DRSRule module is the way to go. I am leaving this post intact for posterity. Enjoy!
End of update - 22 Jan 2015

We, like many, had the need for a way to backup our DRS rules. Searches turned up many pages, all of which had the same basic recipe: use the Get-DrsRule to grab the rules, get a few properties, and export to CSV.

The problem: Addressing the task getting all of the pertinent info for rules of type ClusterVmHostRuleInfo, or, "VM-to-Host" rules. Rules of this type do not have the VM IDs in a property of the rule, but rather have the VMGroupName and HostGroupNames properties. From these properties, and the .NET view object of the given cluster, one can retrieve the VMs' and hosts' info.

So, we did a little work, and wrote the following to, for all DRS rule types, get the information that would be required if one wanted to recreate the rules:
## Script function: Gather/export DRS rule info, including VMGroup and HostGroup members
## Author: vNugglets.com

## file to which to write the DRS rules' info
$strOutputFilespec = "e:\someFolder\DRSrules_export.csv"

## get cluster .NET view objects, which hold the info about the DRS rules
Get-View -ViewType ClusterComputeResource -Property Name, ConfigurationEx | %{
    ## if the cluster has any DRS rules
    if ($_.ConfigurationEx.Rule -ne $null) {
        $viewCurrClus = $_
        $viewCurrClus.ConfigurationEx.Rule | %{
            $oRuleInfo = New-Object -Type PSObject -Property @{
                ClusterName = $viewCurrClus.Name
                RuleName = $_.Name
                RuleType = $_.GetType().Name
                bRuleEnabled = $_.Enabled
                bMandatory = $_.Mandatory
            } ## end new-object

            ## add members to the output object, to be populated in a bit
            "bKeepTogether,VMNames,VMGroupName,VMGroupMembers,AffineHostGrpName,AffineHostGrpMembers,AntiAffineHostGrpName,AntiAffineHostGrpMembers".Split(",") | %{Add-Member -InputObject $oRuleInfo -MemberType NoteProperty -Name $_ -Value $null}

            ## switch statement based on the object type of the .NET view object
            switch ($_){
                ## if it is a ClusterVmHostRuleInfo rule, get the VM info from the cluster View object
                #   a ClusterVmHostRuleInfo item "identifies virtual machines and host groups that determine virtual machine placement"
                {$_ -is [VMware.Vim.ClusterVmHostRuleInfo]} {
                    $oRuleInfo.VMGroupName = $_.VmGroupName
                    ## get the VM group members' names
                    $oRuleInfo.VMGroupMembers = (Get-View -Property Name -Id ($viewCurrClus.ConfigurationEx.Group | ?{($_ -is [VMware.Vim.ClusterVmGroup]) -and ($_.Name -eq $oRuleInfo.VMGroupName)}).Vm | %{$_.Name}) -join ","
                    $oRuleInfo.AffineHostGrpName = $_.AffineHostGroupName
                    ## get affine hosts' names
                    $oRuleInfo.AffineHostGrpMembers = if ($_.AffineHostGroupName -ne $null) {(Get-View -Property Name -Id ($viewCurrClus.ConfigurationEx.Group | ?{($_ -is [VMware.Vim.ClusterHostGroup]) -and ($_.Name -eq $oRuleInfo.AffineHostGrpName)}).Host | %{$_.Name}) -join ","}
                    $oRuleInfo.AntiAffineHostGrpName = $_.AntiAffineHostGroupName
                    ## get anti-affine hosts' names
                    $oRuleInfo.AntiAffineHostGrpMembers = if ($_.AntiAffineHostGroupName -ne $null) {(Get-View -Property Name -Id ($viewCurrClus.ConfigurationEx.Group | ?{($_ -is [VMware.Vim.ClusterHostGroup]) -and ($_.Name -eq $oRuleInfo.AntiAffineHostGrpName)}).Host | %{$_.Name}) -join ","}
                    break;
                } ## end block
                ## if ClusterAffinityRuleSpec (or AntiAffinity), get the VM names (using Get-View)
                {($_ -is [VMware.Vim.ClusterAffinityRuleSpec]) -or ($_ -is [VMware.Vim.ClusterAntiAffinityRuleSpec])} {
                    $oRuleInfo.VMNames = if ($_.Vm.Count -gt 0) {(Get-View -Property Name -Id $_.Vm | %{$_.Name}) -join ","}
                } ## end block
                {$_ -is [VMware.Vim.ClusterAffinityRuleSpec]} {
                    $oRuleInfo.bKeepTogether = $true
                } ## end block
                {$_ -is [VMware.Vim.ClusterAntiAffinityRuleSpec]} {
                    $oRuleInfo.bKeepTogether = $false
                } ## end block
                default {"none of the above"}
            } ## end switch

            $oRuleInfo
        } ## end foreach-object
    } ## end if
} | Export-Csv -NoTypeInformation $strOutputFilespec

This bit of code, using the Get-View cmdlet for max speed and min memory footprint, starts off by getting a couple of select properties for all clusters. If the cluster has any DRS rules defined, then the code, for each rule:
  • creates a new PSObject to hold the pertinent info about the rule
  • includes basic rule info, the rule type, and info about the objects involved with the rule, including the VM group names/members and the host group names/members (for both Affine- and AntiAffine host groups)
  • returns the object
  • exports all of the gathered info to CSV for later consumption

Arnim van Lieshout wrote a good post with more information about DRS rules themselves at Managing VMware DRS rules using PowerCLI, and included a few functions for creating VMGroups ("Virtual Machines DRS Groups" in the vSphere client) and HostGroups ("Host DRS Groups"), and for creating new VM-to-Host rules. We will likely be using said functions for our "recreate the DRS rules/groups" work.

25 July 2011

Setting MAC Address for VM NICs using PowerShell

A bit ago, there were some questions in the VMware VMTN community forums about setting explicit MAC addresses for VMs' NICs.

While this can be done via PowerCLI using the Set-NetworkAdapter cmdlet, the MAC address range is restricted to 00:50:56:00:00:00 - 00:50:56:3F:FF:FF when using the cmdlet, which is only a subset of the range that VMware uses.

An example came up where one might need to set the MAC address to something in a valid VMware range (with VMware OUIs like "00:50:56" or "00:0C:29") outside of the subset above. This can be done using the Web Services API, as it does not have the same range limitation as the cmdlet. The well-commented code below shows how to do so using the API:

## script function:  set the MAC address of a VM's NIC
## Author: vNugglets.com

## the name of the VM whose NIC's MAC address to change
$strTargetVMName = "myVM01"
## the MAC address to use
$strNewMACAddr = "00:50:56:90:00:01"

## get the .NET view object of the VM
$viewTargetVM = Get-View -ViewType VirtualMachine -Property Name,Config.Hardware.Device -Filter @{"Name" = "^${strTargetVMName}$"}
## get the NIC device (further operations assume that this VM has only one NIC)
$deviceNIC = $viewTargetVM.Config.Hardware.Device | Where-Object {$_ -is [VMware.Vim.VirtualEthernetCard]}
## set the MAC address to the specified value
$deviceNIC.MacAddress = $strNewMACAddr
## set the MAC address type to manual
$deviceNIC.addressType = "Manual"

## create the new VMConfigSpec
$specNewVMConfig = New-Object VMware.Vim.VirtualMachineConfigSpec -Property @{
   ## setup the deviceChange object
   deviceChange = New-Object VMware.Vim.VirtualDeviceConfigSpec -Property @{
       ## the kind of operation, from the given enumeration
       operation = "edit"
       ## the device to change, with the desired settings
       device = $deviceNIC
   } ## end New-Object
} ## end New-Object

## reconfigure the "clone" VM
$viewTargetVM.ReconfigVM($specNewVMConfig)


The ReconfigVM() method does not care if the new MAC address is a valid VMware MAC address. It does not even seem to care if the new MAC address string is a valid hex MAC address. So, use with care -- one would not want to break networking on their precious VM by setting its NIC to have an invalid MAC address, or a duplicate, etc.

Other info: the valid VMware OUIs can be found many places, like at the IEEE.org Public Listing search site.


Update 25 Nov 2011:
Do note that changing the MAC address of a VM NIC to something outside of the "accepted" range will cause other PowerCLI cmdlets and the vSphere client to report that the NIC's MAC is "not in the valid range", and may prevent further operations on that VM's NIC with those tools.  Thank-you to the anonymous commenter here and to LucD (in the VMware Communities thread at http://communities.vmware.com/message/1869800#1869800) for highlighting this point.


Update 31 Dec 2011:
The -Filter parameter for the Get-View call on line 10 was using a regular expression that had the possibility of returning more than one VM View object.  I have updated the regular expression to only match VMs of exactly the given name.  Thanks go to Joep Piscaer for pointing out the issue.  The code in this post has been updated.