24 December 2013

Get VM By RDM with PowerCLI

Sometimes one needs to find a VM by RDM.  This is not a new thought, and there are plenty of spots on the internet that give ways to achieve this.  When this need arose for us a couple of years ago, we found no spot that gave a way to achieve this quickly.  So, with vNugglets' focus on speed, I put together a function to do just that:  quickly find the VM that is using a given SAN device as an RDM.

Then, just recently, a VMware Communities forum post made me realize that we had not yet shared this function with the world.  So, here we go:
<#    .Description
    Function to find what VM (if any) is using a LUN as an RDM, based on the LUN's SCSI canonical name. Assumes that the best practice of all hosts in a cluster seeing the same LUNs is followed.
    vNugglets, originally from Nov 2011
    .Example
    Get-VMWithGivenRDM -CanonicalName naa.60000970000192602222333031314444 -Cluster myCluster0 | ft -a
    Find a VM using the given LUN as an RDM, formatting output in auto-sized table.  Output would be something like:
    VMName            VMDiskName     DeviceDisplayName   CanonicalName
    ------            ----------     -----------------   -------------
    myvm033.dom.com   Hard disk 10   myvm033-data3FS     naa.60000970000192602222333031314444
    .Outputs
    Zero or more PSObjects with info about the VM and its RDM
#>
function Get-VMWithGivenRDM {
    param(
        ## Canonical name of the LUN in question
        [parameter(Mandatory=$true)][string[]]$CanonicalNameOfRDMToFind_arr,
        ## Cluster whose hosts see this LUN
        [parameter(Mandatory=$true)][string]$ClusterName_str
    ) ## end param

    ## get the View object of the cluster in question
    $viewCluster = Get-View -ViewType ClusterComputeResource -Property Name -Filter @{"Name" = "^$([RegEx]::escape($ClusterName_str))$"}
    ## get the View of a host in the given cluster (presumably all hosts in the cluster see the same storage)
    $viewHostInGivenCluster = Get-View -ViewType HostSystem -Property Name -SearchRoot $viewCluster.MoRef | Get-Random
    ## get the Config.StorageDevice.ScsiLun property of the host (retrieved _after_ getting the View object for speed, as this property is only retrieved for this object, not all hosts' View objects)
    $viewHostInGivenCluster.UpdateViewData("Config.StorageDevice.ScsiLun")

    ## if matching device(s) found, store some info for later use
    $arrMatchingDisk = &{
    ## get the View objects for all VMs in the given cluster
    Get-View -ViewType VirtualMachine -Property Name, Config.Hardware.Device -SearchRoot $viewCluster.MoRef | %{$viewThisVM = $_
        ## for all of the RDM devices on this VM, see if the canonical name matches the canonical name in question
        $viewThisVM.Config.Hardware.Device | ?{($_ -is [VMware.Vim.VirtualDisk]) -and ("physicalMode","virtualMode" -contains $_.Backing.CompatibilityMode)} | %{
            $hdThisDisk = $_
            $lunScsiLunOfThisDisk = $viewHostInGivenCluster.Config.StorageDevice.ScsiLun | ?{$_.UUID -eq $hdThisDisk.Backing.LunUuid}
            ## if the canonical names match, create a new PSObject with some info about the VirtualDisk and the VM using it
            if ($CanonicalNameOfRDMToFind_arr -contains $lunScsiLunOfThisDisk.CanonicalName) {
                New-Object -TypeName PSObject -Property @{
                    VMName = $viewThisVM.Name
                    VMDiskName = $hdThisDisk.DeviceInfo.Label
                    CanonicalName = $lunScsiLunOfThisDisk.CanonicalName
                    DeviceDisplayName = $lunScsiLunOfThisDisk.DisplayName
                } ## end new-object
            } ## end if
        } ## end foreach-object
    } ## end foreach-object
    } ## end scriptblock

    ## if a matching device was found, output its info
    if ($arrMatchingDisk) {$arrMatchingDisk | Select VMName, VMDiskName, DeviceDisplayName, CanonicalName}
    ## else, say so
    else {Write-Verbose -Verbose "Booo. No matching disk device with canonical name in '$CanonicalNameOfRDMToFind_arr' found attached to a VM as an RDM in cluster '$ClusterName_str'"}
} ## end fn
Some example usage:
PS vN:\> Get-VMWithGivenRDM -CanonicalName naa.60000123412342602222333031314444 -Cluster myMegaCluster

VMName            VMDiskName     DeviceDisplayName   CanonicalName
------            ----------     -----------------   -------------
myvm002.dom.com   Hard disk 13   myvm002-dataFS_7    naa.60000123412342602222333031314444
...

The point of having a Cluster param is to focus the scope of the search (one generally knows to what set of hosts a particular LUN has been presented).

And, some thoughts on how to make this function even better:
  • take cluster input from pipeline
  • add ability to check entire vCenter inventory, but keep things fast by first narrowing scope to only the VMHosts that see given device, then check those hosts' clusters for a VM with RDM using said device (removes need for "Cluster" param)
  • maybe:  add other info to output object, if there are other things that would be useful/valuable
So, how does this do -- FaF for you, too?  Yeah, just as we thought!

No comments:

Post a Comment

Note: Only a member of this blog may post a comment.