29 December 2013

Get VM Disks and RDMs via PowerCLI

Need to get info about a VM's hard disks?  Like, "regular" virtual disks and RDMs, in a handy and fast manner?  We need to, occasionally, and so wrote a function to do so (a while ago -- just finally posting it).  It gets things like the hard disk name, the SCSI ID, the storage device display name, the disk size, the SCSI canonical name, and [optionally] the full datastore path for the disk files.  Here again, not the first bit of code around to retrieve such things, but a version that is written to do things most quickly.

The script:
<# .Description
    Function to get a VM's hard disk and RDM info
    Originally from Sep 2011, updated Dec 2013 -- vNugglets.com
    .Example
    Get-VMDiskAndRDM -vmName someVM -ShowVMDKDatastorePath | ft -a
    VMName HardDiskName ScsiId DeviceDisplayName SizeGB ScsiCanonicalName                    VMDKDStorePath
    ------ ------------ ------ ----------------- ------ -----------------                    --------------
    someVM Hard disk 1  0:0                          50                                      [dstore0] someVM/someVM.vmdk
    someVM Hard disk 2  1:0    someVM-/log_dir       20 naa.60000945618415615641111111111111 [dstore0] someVM/someVM_1.vmdk
    Get the disks (including RDMs) for "someVM", and include the datastore path for each VMDK
#>
function Get-VMDiskAndRDM {
    param(
        ## name pattern of the VM guest for which to get info
        [parameter(Mandatory=$true)][string]$vmName_str = "myVM",
        ## switch to specify that VMDK's datastore path should also be returned
        [switch]$ShowVMDKDatastorePath_sw
    )

    ## the cool, FaF way (using .NET View objects)
    ## get the VM object(s)
    $arrVMViewsForStorageInfo = Get-View -Viewtype VirtualMachine -Property Name, Config.Hardware.Device, Runtime.Host -Filter @{"Name" = "$vmName_str"}
    if (($arrVMViewsForStorageInfo | Measure-Object).Count -eq 0) {Write-Warning "No VirtualMachine objects found matching name pattern '$vmName_str'"; exit} ## end if

    $arrVMViewsForStorageInfo | %{
        $viewVMForStorageInfo = $_
        ## get the view of the host on which the VM currently resides
        $viewHostWithStorage = Get-View -Id $viewVMForStorageInfo.Runtime.Host -Property Config.StorageDevice.ScsiLun

        $viewVMForStorageInfo.Config.Hardware.Device | ?{$_ -is [VMware.Vim.VirtualDisk]} | %{
            $hdThisDisk = $_
            $oScsiLun = $viewHostWithStorage.Config.StorageDevice.ScsiLun | ?{$_.UUID -eq $hdThisDisk.Backing.LunUuid}
            ## the properties to return in new object
            $hshThisVMProperties = @{
                VMName = $viewVMForStorageInfo.Name
                ## the disk's "name", like "Hard disk 1"
                HardDiskName = $hdThisDisk.DeviceInfo.Label
                ## get device's SCSI controller and Unit numbers (1:0, 1:3, etc)
                ScsiId = &{$strControllerKey = $_.ControllerKey.ToString(); "{0}`:{1}" -f $strControllerKey[$strControllerKey.Length - 1], $_.Unitnumber}
                DeviceDisplayName = $oScsiLun.DisplayName
                SizeGB = [Math]::Round($_.CapacityInKB / 1MB, 0)
                ScsiCanonicalName = $oScsiLun.CanonicalName
            } ## end hsh
            ## the array of items to select for output
            $arrPropertiesToSelect = "VMName,HardDiskName,ScsiId,DeviceDisplayName,SizeGB,ScsiCanonicalName".Split(",")
            ## add property for VMDKDStorePath if desired
            if ($ShowVMDKDatastorePath_sw -eq $true) {$hshThisVMProperties["VMDKDStorePath"] = $hdThisDisk.Backing.Filename; $arrPropertiesToSelect += "VMDKDStorePath"}
            New-Object -Type PSObject -Property $hshThisVMProperties | Select $arrPropertiesToSelect
        } ## end foreach-object
    } ## end foreach-object
} ## end function

Some example usage:
PS vN:\> Get-VMDiskAndRDM -vmName myVM01 -ShowVMDKDatastorePath | ft -a

VMName HardDiskName ScsiId DeviceDisplayName SizeGB ScsiCanonicalName                    VMDKDStorePath
------ ------------ ------ ----------------- ------ -----------------                    --------------
myVM01 Hard disk 1  0:0                          50                                      [dstore0] myVM01/myVM01.vmdk
myVM01 Hard disk 2  1:0    myVM01-/data001      660 naa.60000946665554443331111111111111 [dstore0] myVM01/myVM01_1.vmdk


The disks with no values for the DeviceDisplayName or ScsiCanonicalName properties are the "regular" virtual disks, and the others are RDMs.  And, this VM has hard disks on two separate SCSI controllers.

Note:  the vmName parameter is used as a regular expression when getting the .NET View object of the given VM.  As such, one can use a pattern, and can get info on multiple VMs that share the same name pattern.

And, that is that:  some juicy disk/RDM info for VMs, and on the quick!  Thanks, Get-View, for keeping things FaF!  Enjoy

BTW:  related post, Get VM by RDM with PowerCLI:  how to get the VM that is using a particular SAN storage device as an RDM.

18 comments:

  1. Nice Function. I have noticed that there is no output values for DeviceDisplayName or ScsiCanonicalName. I have not gotten any errors so I am not sure if there is something that may need adjusting.

    any thoughts?

    ReplyDelete
    Replies
    1. Hello, Tony-

      Thanks! As for the $null values for the DeviceDisplayName and ScsiCanonicalName properties -- that is due to the type of virtual disk. Those value will be empty for "regular" virtual disks, only populated for RDMs.

      In the example in the post, you see that Hard Disk 1 has no values for those two properties, and Hard Disk 2 has values. That is because that VM has an RDM for Hard Disk 2. The properties are of the SCSI LUN that backs the RDM. Make sense?

      Delete
  2. Thanks for the function, saved me a ton of time writing one up for something similar!

    I wanted to note, that to get exact matches change line 22 to:

    $arrVMViewsForStorageInfo = Get-View -Viewtype VirtualMachine -Property Name, Config.Hardware.Device, Runtime.Host | Where-Object {$_.Name -eq "$vmName_str"}

    This is very useful if you're looping through a large number of VMs where there may be mulitple matches for a given string. It seems to be running a "contains" search as-is.

    ReplyDelete
    Replies
    1. Hello, Brandon-

      Good, glad that it helped!

      As for the "get exact matches change" -- yes, that is a way to get exact matches. Though, it has a downfall of slowing down the show: the -Filter way does the filtering at the vCenter server, whereas the Where-Object way returns the View object for _all_ VMs and _then_ does the filtering at the client side.

      As for getting exact matches with the way that the function is written, you can use standard regular expression anchors. So, if you have myvm1 and myvm10, and want to only get disk/RDM info for myvm1, you can use the following value for the $vmName parameter: "^myvm1$". That means that the VM name must start with the "m" character, and end with the "1" character, and that is the entire pattern (instead of matching in the "contains" style that you mention).

      Btw -- part of the reason for having written the function to use regular expressions was so that people could get clever in any way they desired with regular expressions, instead of constraining them to using standard wildcarding.

      Anyway, give that a shot, and see how it does for you (and, if you have a very large environment/inventory, you should notice the speed difference, too, with the -Filter vs. the Where-Object).

      Thanks for the comment!

      Delete
  3. Excellent Script! Thanks a lot for that!

    I would like to add two other properties from get-harddisk to your script, but can't figure out how:
    the disk ID and the disk type (rawvirtual, rawphysical, flat).

    How would I add those two to your script?

    ReplyDelete
    Replies
    1. Hello, MrSulik-

      Thanks, glad that you are enjoying it! As for adding those other properties that you mentioned, you could add the following few lines into the code at the corresponding spots:
      After Line 42, add:
      Id = "{0}/{1}" -f $viewVMForStorageInfo.MoRef.ToString(), $hdThisDisk.Key
      DiskType = $(
      if ($hdThisDisk.Backing -is [VMware.Vim.VirtualDiskFlatVer2BackingInfo]) {"Flat"}
      elseif ($null -ne (Get-Member -InputObject $hdThisDisk.Backing -Name compatibilityMode)) {"Raw{0}" -f (Get-Culture).TextInfo.ToTitleCase(($hdThisDisk.Backing.compatibilityMode -replace "mode",""))}
      else {$null})



      And, replace the line originally at Line 45 with the following:
      $arrPropertiesToSelect = "VMName,HardDiskName,ScsiId,DeviceDisplayName,SizeGB,ScsiCanonicalName,Id,DiskType".Split(",")


      Enjoy some more!

      Delete
  4. Thanks for sharing scripts,

    I'm trying to pulling out more than one vm information with regular expression like vmName "xx1", "xx2", "xx3" it doesn't work ,
    second things i wanted to pull out DeviceDisplayName and ScsiCanonicalName for regular VMDK

    Greatly appreciate if you can help me out

    ReplyDelete
    Replies
    1. Anon - Here is a known working line in my script that pulled disk info form vNugglets. The "Name"="^[a-zA-Z1-9]" is the regex part. If the vm starts with a zero then I don't want it. You may want ^[xx] starts with xx.

      $vm = get-view -ViewType VirtualMachine -Filter @{“Runtime.PowerState”=”poweredOn”;”Config.Template”=”false”;"Name"="^[a-zA-Z1-9]"} -SearchRoot $(get-view -ViewType Datacenter -Property Name -Filter @{“Name” = “MyDC”}| select -ExpandProperty MoRef)

      Delete
  5. Hello, Can we get VM's each disks UUID..
    Normally we use VMKFSTOOLS command to get ..

    ReplyDelete
    Replies
    1. Hello, Anonymous-

      Surre! We can stay off of the ESXi console and get the disk UUIDs with a minor adjustment to the code in this post. Say, like:

      Insert a new property in the hashtable (say, insert this next line after the current line 37 in the code):
      HardDiskUUID = $hdThisDisk.Backing.LunUuid

      And, update the array of property names to select (replace current line 45 with this):
      $arrPropertiesToSelect = "VMName,HardDiskName,HardDiskUUID,ScsiId,DeviceDisplayName,SizeGB,ScsiCanonicalName".Split(",")

      That should work out just right. Does it?

      Enjoy,

      Matt

      Delete
  6. Sorry newbie here, when running this command with .\ in front (else it says it cannot find) nothing happens, runs very quick with no output, any idea what I'm doing wrong?

    ReplyDelete
  7. Seems if i just put this as the last line in the function and run it in powershell it works.
    Get-VMDiskAndRDM -vmName myvirtualserver -ShowVMDKDatastorePath | ft -a

    However not getting anything back for DeviceDisplayName or ScsiCannoicalName?

    ReplyDelete
    Replies
    1. Hello, Anonymous-

      Looks like you figured out the part of, "after having defined the function in PowerShell session, _then_ invoke the function by name -- call Get-VMDiskAndRDM with parameters".

      As for those two properties having $null values: if you see after the code block in this post, there is a bit about, "The disks with no values for the DeviceDisplayName or ScsiCanonicalName properties are the "regular" virtual disks, and the others are RDMs".

      So, it sounds like the VM "myvirtualserver" only has regular virtual disks (no RDMs). Might that be the case?

      Delete
  8. The SCSI Bus number is stored in the controller's device object. It is just luck that you can usually calculate it from the controller key.

    # before the disk loop
    $devicehash = @{}
    $viewVMForStorageInfo.Config.Hardware.Device | %{$devicehash[$_.Key] = $_}

    #inside the loop
    $ScsiBus = $devicehash[$hdThisDisk.ControllerKey].BusNumber


    I don't have a clean way yet to test for IDE drives.

    ReplyDelete
  9. Thanks for great script,

    Is possible to get value DeviceDisplayName and ScsiCanonicalName for regular disk with VM?

    Regards,

    ReplyDelete
  10. Great script!
    Every time I try to use it on a VM with an asterisk in its name it doesn't work.
    For example: Get-VMDiskAndRDM -vmName_str *vm_name
    Results in:
    Get-View parsing "*vm_name" - Quantifier {x,y} following nothing.
    At E:\PS\functions\get-vmdiskandRDM.ps1:22 char:33
    + ... orageInfo = Get-View -Viewtype VirtualMachine -Property Name, Config. ...
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : NotSpecified: (:) [Get-View], VimException
    + FullyQualifiedErrorId : Core_BaseCmdlet_UnknownError,VMware.VimAutomation.ViCore.Cmdlets.Commands.DotNetInterop.GetVIView

    Any tip?

    ReplyDelete