Get-InventoryPlus – Inventory of all vSphere objects

Often I have to get a complete list of all the objects in a vSphere environment. From the PowerCLI cmdlets, the Get-Inventory cmdlets looks like the obvious candidate to tackle such a request. But the cmdlet seems to have some shortcomings. It definitely does not return all vSphere objects.

Hence I set out to write this Get-InventoryPlus function.

The function was demonstrated for the first time during the 24th VMUGBe in Mechelen.

The Issues

A quick demonstration of the problem. When you do a Get-Inventory, a list of vSphere objects is produced.

inventory-0

But some vSphere objects are definitely not in the list. To make this more visible, I grouped the returned objects by objecttype.

inventory-1

There are no network, nor storage related objects in the list returned by Get-Inventory !

With my Get-InventoryPlus function you do get a complete inventory. Notice how we now get network and storage related objects returned.

inventory-2

And there is in fact a 2nd issue !

Have a look at these two views of the entry named Folder2. Are we looking at one object or two objects ?

folder-10

To take the suspense away, an object of the type Folder can in fact appear twice in your inventory, and they are not the same object ! These are the so-called “Yellow” and “Blue” folders. They can have the same name, and appear to be in the same location. But in fact they are not in the same location. It’s an optical illusion, that is caused by the hidden folders ! In this case the hidden folders named ‘host’ and ‘vm’

folder-12

The view in old C# client makes this more visible.

folder-11

So I had to make sure that the function could make the distinction between the two !

The script

Annotations

Line 29: By default the function gets the inventory of the vSphere server that is in $Global:DefaultVIServer, but you can ask for the inventory of another vSphere server with this Server parameter. Notice the Type used for the Server parameter, it is the “portable” way of specifying a Type, as described in PowerCLI Best Practice: Correct Use of Strong Typing.

Line 33, 81: The Get-InventoryPlus function uses two inline, helper functions, Get-ViBlueFolderPath and Get-ObjectInfo.

Line 36: Since the helper function can be called with different types of vSphere objects, the $Itemp parameter is defined as an object of the base ManagedEntity type. All derived Types will be accepted.

Line 42,46,58,63,102,113: Depending on the type of object, the path upwards needs to use different properties. That is where the UpdateViewData method comes in handy, to just add the required property.

Line 73: The function traverse the path bottom up, as a consequence the elements in the $path array needs to be reversed, before the path string can be created.

Line 89: The script uses an array to hold the names of all hidden folders

Line 135-138: if an object has a “yellow” and a “blue” path that are the same, the BluePath property is retained, and the Path property is set to the NoValue string

Line 139-141: a Template object, by definition only has a BluePath

Line 147-152: All vSphere information can be retrieved starting from the ServiceInstance object

Line 155: The ContainerView is the simplest method to find all vSphere objects

Sample Usage

The usage is rather straight-forward.

Will produce something similar to this

inventoryplus-1

A call of the Get-InventoryPlus function will produce a number of objects. Each object has sufficient properties to uniquely distinguish it in the vSphere environment.

I tested the function in my lab against many possible configurations, but it is always possible I missed a “special” case. If you should encounter such an issue, please let me know.

 

Enjoy!

22 Comments

    Drew

    hi LucD,

    will this work with vSphere 6.7? I’m not getting any output from the command when I run it after connecting PowerCLI to my vCenter.

    thanks!
    Drew

      LucD

      Hi Drew,
      I don’t have 6.7 anymore, but just tested it in 7, and it works for me.

      Did you call the function as shown in Sample Usage?

        Drew

        oh ok. then it should be working.

        I’ve tried a number of different ways, the command standalone, and with the ./ prefix. Tried Griswold’s method of running the ps1, then connecting the vcenter, then running the command again without the prefix or suffix. didn’t work.

          LucD

          You copy the complete function in a .ps1 file.
          Then at the end of that .ps1 file, add a line that calls the function.
          Then just run the .ps1 file from the PS prompt (make sure you are connected to a vCenter before)

    Shraga

    Hi,
    Using the script above, getting the following error:
    Line |
    124 | $Object = Get-View -Id $Parent -Property Name,Parent
    | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    | Cannot convert the “System.Object[]” value of type “System.Object[]” to type
    | “VMware.Vim.ManagedEntity”.

    I verified that $parent getting the group-folder value, so not sure why it’s crashing on it. Can you advise on it?

      LucD

      Hi,
      The error states that $Parent contains more than 1 object.

      Which PowerCLI version are you using?
      Newer versions do allow an array on the Id parameter?

      Also, are you perhaps connected to more than 1 vSphere Server?
      Check what is in $global:defaultVIServers.

        Shraga

        Thanks LucD,
        Your hint was correct, i’d 2 servers connected. Once i disconnected all and triggered again the process, the report was created.

        Thanks a lot, you’re really appreciated for your help.

    David Griswold

    I have made additional additions, both internal and external to the script. I capture the name of the vCenter as well in each returned object, and the relationship to all other objects, such as VMs, Host, Datastores, etc. Then, using the ‘Write-ObjectToSQL’ modules, I can quickly save the results to a SQL table. Next, I loop through each object (there are 4700 on our main vCenter server, and we have 17 vCenters) and save the relationships of the subordinate objects to another table. By collecting and saving the vCenter name, I can differentiate if an object in two vCenters has the same object-id, by referencing both the object-id and the vCenter name.

    What I am getting to, is to ask if this function is on GIT or some other source code management system. I would like to post my updates as a fork.

      LucD

      Hi David,
      No, I haven’t at the moment.
      But I was thinking about creating a repository for the functions I publish on my blog.

      I was wondering, would you, as a user, prefer a module or a loose collection of scripts and functions?
      The latter would be more fit to also include sample scripts.

        David Griswold

        Personally, I tend to roll my own, using other’s scripts as a starting place or inspiration. However, I think that a lot of your scripts/functions (and mine) would make a great module for a broader audience. So, to answer your question, both 🙂

        I’d like to use https://github.com/vmware

    Mathijs

    hi LucD, this helped me so much, thanks for sharing!

    Chris

    Hi there,

    Could you please explain how to execute this? I get the same as HULK

    thank you

      LucD

      Hi Chris,
      I assume that you placed the function in a .ps1 file?
      When you run that .ps1 file from the PS prompt, the PS engine “knows” the function.

      You can now call the function, by just entering

      Get-InventoryPlus

      at the PS prompt.

      Hope this helps.

    djianto

    hi Luc,
    I was wondering how we could also list the IP address and Guest OS types with this function. I am just starting to learn powerCLI. Thank you and keep up the good work!

      LucD

      Hi,
      Sure that is possible.
      But note that this will only work for VMs that have the VMware Tools running in their Guest OS.
      Try with this small change to the function.

      function Get-InventoryPlus
      {
      <# .SYNOPSIS Retrieve the objects available on a vSphere Server. .DESCRIPTION This function will retrieve all objects available on a vSphere Server, including all storage and network objects. .NOTES Author: Luc Dekens .PARAMETER Server The vSphere Server (vCenter or ESXi) from which to retrieve the objects. The default is $Global:DefaultVIServer .PARAMETER NoValue Properties that are not set are returned as an empty string. The value on this parameter will be used instead of the empty string. .EXAMPLE PS> Get-InventoryPlus
      .EXAMPLE
      PS> Get-InventoryPlus -Server $vCenter
      .EXAMPLE
      PS> Get-InventoryPlus -NoValue 'na'
      #>

      [cmdletbinding()]
      param(
      [VMware.VimAutomation.ViCore.Types.V1.VIServer]$Server = $Global:DefaultVIServer,
      [String]$NoValue = ''
      )

      function Get-ViBlueFolderPath{
      [cmdletbinding()]
      param(
      [VMware.Vim.ManagedEntity]$Item
      )

      $hidden = 'Datacenters','vm'

      if($Item -is [VMware.Vim.VirtualMachine]){
      $Item.UpdateViewData('Parent')
      $parent = $Item.Parent
      }
      elseif($Item -is [VMware.Vim.VirtualApp]){
      $Item.UpdateViewData('ParentFolder')
      $parent = $Item.ParentFolder
      }

      if($parent){
      $path = @($Item.Name)
      while($parent){
      $object = Get-View -Id $parent -Property Name,Parent
      if($hidden -notcontains $object.Name){
      $path += $object.Name
      }
      if($object -is [VMware.Vim.VirtualApp]){
      $object.UpdateViewData('ParentFolder')
      if($object.ParentFolder){
      $parent = $object.ParentFolder
      }
      else{
      $object.UpdateViewData('ParentVapp')
      if($object.ParentVapp){
      $parent = $object.ParentVapp
      }
      }
      }
      else{
      $parent = $object.Parent
      }
      }
      [array]::Reverse($path)
      return "/$($path -join '/')"
      }
      else{
      return $NoValue
      }
      }

      function Get-ObjectInfo{
      [cmdletbinding()]
      param(
      [parameter(ValueFromPipeline)]
      [VMware.Vim.ManagedEntity]$Object
      )

      Begin{
      $hidden = 'Datacenters','vm','host','network','datastore','Resources'
      }

      Process{
      if($hidden -notcontains $Object.Name){
      $props = [ordered]@{
      Name = $Object.Name
      Type = $Object.GetType().Name
      BluePath = $NoValue
      GuestOS = ''
      IP = ''
      }
      $blueFolder = $false
      $isTemplate = $false
      if($object -is [VMware.Vim.Folder]){
      $object.UpdateViewData('ChildType')
      if($Object.ChildType -contains 'VirtualMachine'){
      $blueFolder = $true
      }
      }
      $path = @($Object.Name)
      $parent = $Object.Parent

      if($object -is [VMware.Vim.VirtualMachine] -or $object -is [VMware.Vim.VirtualApp]){
      $props['BluePath'] = Get-VIBlueFolderPath -Item $Object
      if($Object -is [VMware.Vim.VirtualMachine]){
      $Object.UpdateViewData('ResourcePool','Config.Template')
      if($Object.Config.Template){
      $parent = $Object.Parent
      $props['Type'] = 'Template'
      $isTemplate = $true
      }
      else{
      $Object.UpdateViewData('Guest.IpAddress','Guest.GuestFullName')
      $props['GuestOS'] = $Object.Guest.GuestFullName
      $props['IP'] = $Object.Guest.IpAddress
      $parent = $Object.ResourcePool
      }
      }
      }
      while($parent){
      $Object = Get-View -Id $Parent -Property Name,Parent
      $parent = $Object.Parent
      if($hidden -notcontains $Object.Name){
      $path += $Object.Name
      }
      }
      [array]::Reverse($path)
      $path = "/$($path -join '/')"
      $props.Add('Path',$path)

      if($blueFolder){
      $props['BluePath'] = $props['Path']
      $props['Path'] = $NoValue
      }
      if($isTemplate){
      $props['Path'] = $NoValue
      }
      New-Object PSObject -Property $props
      }
      }
      }

      $sView = @{
      Id = 'ServiceInstance'
      Server = $Server
      Property = 'Content.ViewManager','Content.RootFolder'
      }
      $si = Get-view @sView
      $viewMgr = Get-View -Id $si.Content.ViewManager

      $contView = $viewMgr.CreateContainerView($si.Content.RootFolder,$null,$true)
      $contViewObj = Get-View -Id $contView

      Get-View -Id $contViewObj.View -Property Name,Parent |
      where{$hidden -notcontains $_.Name} |
      Get-ObjectInfo
      }

        Jeffrey

        Hi, sorry to bother you, but would you mind explaining how to modify your script to add a column for the VM id? I was using your script along with the following where clause to filter only VMs.

        Get-InventoryPlus | where{$_.Type -eq ‘VirtualMachine’}

        However, since a VM name might not be unique, I’d like an ID column as well that is specific to each VM. Any help would be appreciated!

          David Griswold

          I did this for all objects. See the ‘Object’ property added to the $props object, below.

          function Get-ObjectInfo{
          [cmdletbinding()]
          param(
          [parameter(ValueFromPipeline)]
          [VMware.Vim.ManagedEntity]$Object
          )

          Begin{
          $hidden = ‘Datacenters’,’vm’,’host’,’network’,’datastore’,’Resources’
          }

          Process{
          if($hidden -notcontains $Object.Name){
          $props = [ordered]@{
          Name = $Object.Name
          Type = $Object.GetType().Name
          BluePath = $NoValue
          Object = $Object.MoRef.ToString().Replace(($Object.GetType().Name).ToString() + “-“, “”)
          }…

            LucD

            Nice extension, thanks for sharing that.

    Raphael

    You’re the genius !! Worked for me !!! Many thx

    Jeffrey

    Hi Luc,

    I had a quick question about modifying / using your function. My objective is to export a list of all VMs, their blue and yellow paths, and certain VM properties such as power state and host name.

    I am able to export the typical VM properties using “Get-VM | Select Name, PowerState, VMHost, VMHostId | Export-Csv -Path $csv -NoTypeInformation -UseCulture”. I am also able to export your function’s paths using “Get-InventoryPlus | where{$_.Type -eq ‘VirtualMachine’} | Export-Csv -Path C:\Temp\Task1.csv -NoTypeInformation -UseCulture”.

    However, I am unsure how to combine these. If it’s not too much trouble, it’d be awesome if you could explain how to export a single csv file with columns for Name, Host, PowerState, BluePath, and YellowPath.

    P.S.: I’m relatively new to PowerShell and PowerCLI, so apologies if this is question is trivial. I came across this function the other day and really appreciate it! I also noticed you are the lead author of the PowerCLI Reference book, which is awesome!

    hulk

    Please update on how to execute this code tried from cli ..when i execute it just goes back to the prompt dont get any output..

    ./inventoryplus.ps1 ,
    Get-InventoryPlus

      David Griswold

      like this…

      . .\Get-InventoryPlus.ps1

      then, after you have used connect-viserver to connect to your vCenter…

      Get-InventoryPlus

Leave a Reply

Your email address will not be published. Required fields are marked *

*
*

This site uses Akismet to reduce spam. Learn how your comment data is processed.