Search code examples
powershellpowercli

All VMs PowerCLI Info output /Console and Custom Object Not Working Properly


I want to to get all our VMs with the vSphere PowerCLI and get those outputed with some additional info. But i don't get the output working properly.

I tried it as Console output already but then i dont get the name with the os and IPAddress

$Object = New-Object PSObject -Property @{
    Name = $name
    OS = $info.OSFullName
    IP = $info.IPAddress
    }

Connect-VIServer -Server VMServer -Credential admin -Password ASecretPWnoOneKnows

$allnames = Get-VM

foreach($name in $allnames)
{
    $info = Get-VMGuest $name | select OSFullName, IPAddress
    Add-Member -InputObject $Object
}

This is my "best approach" so far.

My result should look like

SomeVMname SomeOS SomeIP

but I only got so far the Length of the total String or error as I don't handle the custom object properly:

Therefore if someone can help me either way, Console output or into a object which i can also export to csv I would be really happy with it


Solution

  • You are very close to making this work already. You just need to move the $object creation and retrieval inside of your ForEach loop.

    Connect-VIServer -Server VMServer -Credential admin -Password ASecretPWnoOneKnows
    $allnames = Get-VM
    
    $Output = foreach($name in $allnames)
    {
      $Object = [PSCustomObject][ordered]@{
        Name=$name.Name
        OS=$name.Guest.OSFullName
        IP=$name.Guest.IPAddress -match "\." -join " "
      }
      $Object
    }
    $Output # To output to the console
    $Output | Export-Csv -path "file.csv" -NoTypeInformaton # Csv output
    

    AVShalom's helpful answer alludes to some of the ideas regarding handling the IPAddress property and the removal of unnecessary commands, i.e. Get-VMGuest, to reach the desired goal. He uses a shortcut method for creating the object $row that will hold the virtual machine properties. However, there are inherent inefficiencies with using array replacement plus addition technique $Report += Row because a new array object of larger size is created each time the loop iterates. For smaller data sets, this may not matter, but for larger ones, this could prove significant.

    Once the [PSCustomObject] is created, you can edit the value in the properties as long as that object is not destroyed and/or those property definitions remain on the object. This means you can have all new values (if you wish) for the same properties during each iteration of the loop. I added the [ordered] attribute so that the $object property order would remain consistent in your output.

    I added the $Output variable to store the value of $Object during each iteration of the loop. $Output becomes an array of objects with the three properties you want to track. Now your Export-Csv will convert those property values into strings for your CSV file.

    I removed the Get-VMGuest command because the $name variable is an object that contains all the information you already need. The Guest property provides the information that Get-VMGuest provides.

    You could cast the IPAddress value to string because there could be times where there is more than one IP since that property is an array. You can choose to grab just an indexed value ($Name.Guest.IPAddress[0]); however, you must be careful with this because the IP address(es) you want, may be at another index. The [string] cast will return all values of that property using a space delimiter. I used the -join operator instead and used a space delimiter. IPAddress may also contain IPv6 addresses. I only wanted to capture IPv4 addresses, so I used a Regex match, e.g. $Name.Guest.IPAddress -match "\.", which matches the literal . character. You can combine multiple techniques to get the data you want.

    Consider the following object and examples:

    $name.guest
    
    IPAddress                                     OSFullName
    ---------                                     ----------
    {1.1.1.1, 2.2.2.2, fe80::ffff:ffff:ffff:ffff} Windows StackOverflow
    
    $name.guest.IPAddress # Retrieves all IPs
    1.1.1.1
    2.2.2.2
    fe80::ffff:ffff:ffff:ffff
    
    $name.guest.IPAddress[0] # Retrieves the first IP in the array
    1.1.1.1
    
    $name.guest.IPAddress -match "\." # Retrieves all IPv4 IPs
    1.1.1.1
    2.2.2.2
    
    $name.guest.IPAddress -match "\." -join " " # Retrieves all IPv4 separated by space
    1.1.1.1 2.2.2.2
    

    With the way your code was structured, it appeared as though you were trying to create properties whose values are by reference rather than by value. To explain further, it appears that you want the property values for $Object to update as the $info property values change automatically without an explicit assignment. I do not know if that was your intention, but I believe that would add complexity to what you want to accomplish. I did not do this for your code. I am setting the $Object property values explicitly each time the loop iterates.

    I also did not see your Export-Csv command; however, you described having an output of the Length property. That can happen when you Export-Csv with a [string] object as the input object. Since the purpose of Export-Csv is to convert property values into strings, the only property value of a regular string is Length. So that is to be expected. You can see this behavior below:

    $str = "this is a string"
    ($str | Get-Member).where({$_.MemberType -eq "Property"})
    
    
       TypeName: System.String
    
    Name   MemberType Definition
    ----   ---------- ----------
    Length Property   int Length {get;}
    $str | ConvertTo-Csv
    #TYPE System.String
    "Length"
    "16"