VMware Cloud Community
Fjorko
Contributor
Contributor
Jump to solution

Need help with this script please...LucD ? :-)

Everything works as expected, but I have an issue with two parts of the script. They are the bits where it itterates the NIC and Hard Drives ( Pink and Blue sections below ).

Problem with the NIC loop ( Pink ) - It only shows nic 0, and if there are multiple nics - they are not output in the resulting CSV file. Also only returns NIC 0 MAC address and non of the other nic's MAC addresses.....

Problem with the disk loop ( Blue ) - it only returns the first TWO disks even though some of my VM's have 4 or even 5 disks...

Any idea why this is happening. This script is not my own work, but I have found it very useful for what I need...

Script :

Clear

$VirtualCenter = Read-Host "Please enter the name of the Virtual Center Server"

$FileLocation = Read-Host "Please Enter Complete Path and file name to save the output.  Must end in .csv or .txt"

$Cred = Get-Credential

Connect-VIServer $VirtualCenter -Credential $Cred

$stats = @()

#Uncomment the next two lines if you would like to inventory just a cluster instead of all VMs in vCenter

#$VMCluster = Read-Host "Please enter the name of the HA Cluster"

#$ServerList = Get-VM -Location $VMCluster

#If the two lines abover are uncommented, then comment the next line

$ServerList = Get-VM

Foreach ($Guests in $ServerList) {

    $Guest = $Guests.Name.ToUpper()

    Write-Progress -Activity "Creating VMware Guest Inventory" -Status "Processing VM Guest $Guest"# Display progress bar

    $VMGuest = Get-VM $Guest | Get-View

    $VM = Get-VM $Guest

    $ESXHost = (Get-VM $Guest).Host.Name.ToUpper()

    $VMHost = get-vmhost $ESXHost | Get-View

    $row = New-Object System.Object

    $row | Add-Member -Type NoteProperty -Name "Guest" -Value $VMGuest.Name.ToUpper()

    $row | Add-Member -Type NoteProperty -Name "Power State" -Value $VM.Guest.State

    $row | Add-Member -Type NoteProperty -Name "Guest OS Full Name" -Value $VM.Guest.OSFullName

    $row | Add-Member -Type NoteProperty -Name "Guest RAM (MB)" -Value $VM.MemoryMB

    $row | Add-Member -Type NoteProperty -Name "Guest vCPU Count" -Value $VM.NumCPU

    $row | Add-Member -Type NoteProperty -Name "Guest VMTools Status" -Value $VMGuest.Guest.ToolsStatus

    $row | Add-Member -Type NoteProperty -Name "Guest VMTools Version" -Value $VMGuest.Guest.ToolsVersion

    $row | Add-Member -Type NoteProperty -Name "Guest VMTools Version Status" -Value $VMGuest.Guest.ToolsVersionStatus

    $row | Add-Member -Type NoteProperty -Name "Guest VMTools Running Status" -Value $VMGuest.Guest.ToolsRunningStatus

   

   $NICCount = 0

    ForEach ($vNic in $VM.Guest.Nics){

        

        $NIC_IP = "Guest IP for NIC " + $NICCount + ""

        $NIC_MAC = "Guest MAC Address for NIC " + $NICCount + ""

        $NIC_vSwitch = "Guest vSwitch Network for NIC " + $NICCount + ""

        $row | Add-Member -Type NoteProperty -Name $NIC_IP -Value $VMGuest.Guest.IpAddress

        $row | Add-Member -Type NoteProperty -Name $NIC_MAC -Value $vNic.MacAddress

        $row | Add-Member -Type NoteProperty -Name $NIC_vSwitch -Value $vNic.NetworkName

        $NICCount++

    }

     $DiskCount = 0

    $DT = @()

    ForEach ($vDisk in $VM.Guest.Disks) {

        $DriveLetter = "Guest Drive " + $DiskCount + ""

        $DriveSize = "Guest Drive " + $DiskCount + " Size"

        $DriveFree = "Guest Drive " + $DiskCount + " Free Space"

        $vDiskCap = [math]::Round(($vDisk.Capacity) / 1GB)

        $vDiskFree = [math]::Round(($vDisk.FreeSpace) / 1GB)

        $row | Add-Member -Type NoteProperty -Name $DriveLetter -Value $vDisk.Path

        $row | Add-Member -Type NoteProperty -Name $DriveSize -Value $vDiskCap

        $row | Add-Member -Type NoteProperty -Name $DriveFree -Value $vDiskFree

        $DiskCount++

        $DriveTotals = "" + $row.$DriveLetter + " " + $row.$DriveSize + ";"

        $DT += $DriveTotals

        }


    $row | Add-Member -Type NoteProperty -Name "Host Name" -Value $VMHost.Summary.Config.Name.ToUpper()

    $row | Add-Member -Type NoteProperty -Name "# of Sessions on Host" -Value $VMHost.vm.Count

    $row | Add-Member -Type NoteProperty -Name "Host is Member of Cluster" -Value (Get-Cluster -VMHost $ESXHost).Name.ToUpper()

    $row | Add-Member -Type NoteProperty -Name "Host Vendor" -Value $VMHost.Hardware.SystemInfo.Vendor

    $row | Add-Member -Type NoteProperty -Name "Host Model" -Value $VMHost.Hardware.SystemInfo.Model

    $HostRam = [math]::Round(($VMHost.Summary.Hardware.MemorySize) / 1GB)

    $row | Add-Member -Type NoteProperty -Name "Host RAM" -Value $HostRam

    $row | Add-Member -Type NoteProperty -Name "Host CPU Model" -Value $VMHost.Summary.Hardware.CpuModel

    $row | Add-Member -Type NoteProperty -Name "Host CPU Count" -Value $VMHost.Summary.Hardware.NumCpuThreads

    $row | Add-Member -Type NoteProperty -Name "Host CPU Speed" -Value $VMHost.Summary.Hardware.CpuMhz

    $row | Add-Member -Type NoteProperty -Name "Host Product Name" -Value $VMHost.Summary.Config.Product.Name

    $row | Add-Member -Type NoteProperty -Name "Host Product Version" -Value $VMHost.Summary.Config.Product.Version

    $row | Add-Member -Type NoteProperty -Name "Host Product Build" -Value $VMHost.Summary.Config.Product.Build

    $row | Add-Member -Type NoteProperty -Name "Host Service Console" -Value $VMHost.Config.Network.ConsolevNic[0].Spec.IP.IPAddress

    $row | Add-Member -Type NoteProperty -Name "Host Service Console Subnet Mask" -Value $VMHost.Config.Network.ConsolevNic[0].Spec.IP.SubnetMask

    $row | Add-Member -Type NoteProperty -Name "Host Service Console 1" -Value $VMHost.Config.Network.ConsolevNic[1].Spec.IP.IPAddress

    $row | Add-Member -Type NoteProperty -Name "Host Service Console 1 Subnet Mask" -Value $VMHost.Config.Network.ConsolevNic[1].Spec.IP.SubnetMask

    $row | Add-Member -Type NoteProperty -Name "Host vMotion IP Address" -Value $VMHost.Config.vMotion.IPConfig.IpAddress

    $row | Add-Member -Type NoteProperty -Name "Host vMotion Subnet Mask" -Value $VMHost.Config.vMotion.IPConfig.SubnetMask

   

    $stats += $row

   

   

}

$stats | Export-Csv -Force .\$FileLocation -NoTypeInformation

Invoke-Item .\$FileLocation

Thanks for your help

PS. I'm a complete NOOB with Powercli, so trying to find my way around it still.

PPS - If anyone has an even better VM Inventory script - please be so kind as to pointing me to it....

0 Kudos
1 Solution

Accepted Solutions
LucD
Leadership
Leadership
Jump to solution

Thought about that as well, but I'm afraid it wil not cover all possibilities.

Assume the following:

VM1, 3 NICs, 2 hard disks

VM2, 2 NICs, 5 hard disks

The order will be VM1, VM2 with the effect that for VM2 you will only get 2 hard disks.

The alternative I came up with us to make dummy entries and always use the maximum columns for NICs and hard disks.

So you would get

       NIC1 NIC2 NIC3 HD1 HD2 HD3 HD4 HD5    

VM1      x    x   x    x   x   na  na  na

VM2      x    x   na   x   x   x   x   x

See if that works for you.


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

View solution in original post

0 Kudos
21 Replies
FranckRookie
Leadership
Leadership
Jump to solution

Hi Fjorko,

I'm not much more experienced but I have a script that makes a report of my infrastructure and I export IP addresses of the VMs with this loop:

  • for ($i=0;$i -lt $_.guest.IPaddress.length ; $i++) {$vms.IPAddress+= " "+$_.guest.IPaddress[$i]}

It gives me a string with all addresses separated by spaces.

Hope it helps.

Regards

Franck

0 Kudos
LucD
Leadership
Leadership
Jump to solution

I reworked your script a bit for efficiency (eliminate the several identical calls to Get-VM).

The reason why you only see 2 guest disks is because of the way the Export-Csv cmdlet works.

It looks at the first row of the array you feed into the cmdlet, and uses that row to determine how many columns there will be in the CSV file.

If you're first guest happens to have 2 guest disks, all the other guests will also only show 2 guest disks in the CSV file, even if they have more guest disks.

Try the attached version of the script.


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

0 Kudos
Fjorko
Contributor
Contributor
Jump to solution

Thanks so much for all your help..

I do however get the following error now :

Exception calling "Join" with "2" argument(s): "Value cannot be null.
Parameter name: value"
At C:\Scripts\Powershell\VmInventory.ps1:39 char:76
+         $row | Add-Member -Type NoteProperty -Name $NIC_IP -Value ([string]::Join <<<< (',',$vNIC.IpAddress))
    + CategoryInfo          : NotSpecified: (:) [], MethodInvocationException
    + FullyQualifiedErrorId : DotNetMethodException

Thanks

0 Kudos
LucD
Leadership
Leadership
Jump to solution

You probably have 1 or more guests that have no NIC, or where the VMware Tools are not installed, or where the guest has been powered off for a longer time.

Try replacing this line

$row | Add-Member -Type NoteProperty -Name $NIC_IP -Value ([string]::Join(',',$vNIC.IpAddress))

by this

$row | Add-Member -Type NoteProperty -Name $NIC_IP -Value (&{if($vNIC.IpAddress){[string]::Join(',',$vNIC.IpAddress)}})


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

0 Kudos
Fjorko
Contributor
Contributor
Jump to solution

OK it's not throwing the error anymore - thanks for that

How do I ensure that a VM with at least two NICS is first in the list ( some sort of check ? ) when the Expot to CSV happens. I understand now what you mean by that if the first VM in the list has less disks/nics than the others, it will only provide so many columns as the first one in the list. I now need to make sure the vm with the most disks and NICS  is first in the list - or at least "reserve" X amount of columns

Any help is appreciated.

Thanks

0 Kudos
omarr1124
Contributor
Contributor
Jump to solution

LucD, I wrote the original script, and I am honored to have you clean it up and to point out the cause for the issue that I had been seeing.  I will work on a way to pad it to ensure that it includes all of the drives and NICS.

0 Kudos
Fjorko
Contributor
Contributor
Jump to solution

Hi Omar

Yeah - all credit to you dude ! It's an awesome script, hope you don't mind me using it in my environment, i just need the NICS and DISKS issue sorted and then it would be the ideal script for me !

Please let me know how and when it is fixed, I'd like to learn how you did it .....

Thanks again !

0 Kudos
LucD
Leadership
Leadership
Jump to solution

Replace this line


$stats | Sort-Object -Property {(Get-Member -InputObject $_).Count} -Descending | Export-Csv .\$FileLocation -NoTypeInformation -Force -UseCulture 

with this

$stats | Sort-Object -Property {(Get-Member -InputObject $_ -Name "Guest MAC*").Count} -Descending | Export-Csv .\$FileLocation -NoTypeInformation -Force -UseCulture 

It will sort the rows, in descending order, based on the number of NICs in a guest


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

Fjorko
Contributor
Contributor
Jump to solution

Cool ! It works ! Getting closer......but

Now the Vm with two nics ( at the top ) only has two disks so other VM's with more than 2 disks only shows 2 ( as you said they would ), so - I need to sort them by NICS AND then also by HDD count  <-- I guess the drive letter would be the best hook to get the count of the amount of HHD right ?

I just do not know how to code the sorting of two properties ( if that is even possible )....

Thanks !!!

0 Kudos
Fjorko
Contributor
Contributor
Jump to solution

I think I may have figured it out. Can now see all NICS and Drives. Here is the export command :

$stats | Sort-Object -Property {(Get-Member -InputObject $_ -Name "Guest MAC*").Count},{(Get-Member -InputObject $_ -Name "Drive Letter*").Count} -Descending | Export-Csv .\$FileLocation -NoTypeInformation -Force -UseCulture

PS. I changed the Name property for the drive letter to "Drive Letter" from the original script, so I can target it as a unique property.

0 Kudos
LucD
Leadership
Leadership
Jump to solution

Thought about that as well, but I'm afraid it wil not cover all possibilities.

Assume the following:

VM1, 3 NICs, 2 hard disks

VM2, 2 NICs, 5 hard disks

The order will be VM1, VM2 with the effect that for VM2 you will only get 2 hard disks.

The alternative I came up with us to make dummy entries and always use the maximum columns for NICs and hard disks.

So you would get

       NIC1 NIC2 NIC3 HD1 HD2 HD3 HD4 HD5    

VM1      x    x   x    x   x   na  na  na

VM2      x    x   na   x   x   x   x   x

See if that works for you.


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

0 Kudos
Fjorko
Contributor
Contributor
Jump to solution

That worked perfectly, however - all the entries below those two loops are now not populating. I checked for syntax errors but cannot see any....

These bits aren't returning anything it seems :

$row | Add-Member -Type NoteProperty -Name "Host Name" -Value $VMHost.Summary.Config.Name.ToUpper()
$row | Add-Member -Type NoteProperty -Name "# of Sessions on Host" -Value $VMHost.vm.Count
$row | Add-Member -Type NoteProperty -Name "Host is Member of Cluster" -Value (Get-Cluster -VMHost $ESXHost).Name.ToUpper()
$row | Add-Member -Type NoteProperty -Name "Host Vendor" -Value $VMHost.Hardware.SystemInfo.Vendor
$row | Add-Member -Type NoteProperty -Name "Host Model" -Value $VMHost.Hardware.SystemInfo.Model
$HostRam = [math]::Round(($VMHost.Summary.Hardware.MemorySize) / 1GB)
$row | Add-Member -Type NoteProperty -Name "Host RAM" -Value $HostRam
$row | Add-Member -Type NoteProperty -Name "Host CPU Model" -Value $VMHost.Summary.Hardware.CpuModel
$row | Add-Member -Type NoteProperty -Name "Host CPU Count" -Value $VMHost.Summary.Hardware.NumCpuThreads
$row | Add-Member -Type NoteProperty -Name "Host CPU Speed" -Value $VMHost.Summary.Hardware.CpuMhz
$row | Add-Member -Type NoteProperty -Name "Host Product Name" -Value $VMHost.Summary.Config.Product.Name
$row | Add-Member -Type NoteProperty -Name "Host Product Version" -Value $VMHost.Summary.Config.Product.Version
$row | Add-Member -Type NoteProperty -Name "Host Product Build" -Value $VMHost.Summary.Config.Product.Build
$row | Add-Member -Type NoteProperty -Name "Host Service Console" -Value $VMHost.Config.Network.ConsolevNic[0].Spec.IP.IPAddress
$row | Add-Member -Type NoteProperty -Name "Host Service Console Subnet Mask" -Value $VMHost.Config.Network.ConsolevNic[0].Spec.IP.SubnetMask
$row | Add-Member -Type NoteProperty -Name "Host Service Console 1" -Value $VMHost.Config.Network.ConsolevNic[1].Spec.IP.IPAddress
$row | Add-Member -Type NoteProperty -Name "Host Service Console 1 Subnet Mask" -Value $VMHost.Config.Network.ConsolevNic[1].Spec.IP.SubnetMask
$row | Add-Member -Type NoteProperty -Name "Host vMotion IP Address" -Value $VMHost.Config.vMotion.IPConfig.IpAddress
$row | Add-Member -Type NoteProperty -Name "Host vMotion Subnet Mask" -Value $VMHost.Config.vMotion.IPConfig.SubnetMask
0 Kudos
RvdNieuwendijk
Leadership
Leadership
Jump to solution

If you want to use the Add-Member cmdlet I think the piece of code in your last post should be written as:

$HostRam = [math]::Round(($VMHost.Summary.Hardware.MemorySize) / 1GB)
$row = $row | Add-Member -Type NoteProperty -Name "Host Name" -Value $VMHost.Summary.Config.Name.ToUpper() -PassThru | `
  Add-Member -Type NoteProperty -Name "# of Sessions on Host" -Value $VMHost.vm.Count -PassThru | `
  Add-Member -Type NoteProperty -Name "Host is Member of Cluster" -Value (Get-Cluster -VMHost $ESXHost).Name.ToUpper() -PassThru | `
  Add-Member -Type NoteProperty -Name "Host Vendor" -Value $VMHost.Hardware.SystemInfo.Vendor -PassThru | `
  Add-Member -Type NoteProperty -Name "Host Model" -Value $VMHost.Hardware.SystemInfo.Model -PassThru | `
  Add-Member -Type NoteProperty -Name "Host RAM" -Value $HostRam -PassThru | `
  Add-Member -Type NoteProperty -Name "Host CPU Model" -Value $VMHost.Summary.Hardware.CpuModel -PassThru | `
  Add-Member -Type NoteProperty -Name "Host CPU Count" -Value $VMHost.Summary.Hardware.NumCpuThreads -PassThru | `
  Add-Member -Type NoteProperty -Name "Host CPU Speed" -Value $VMHost.Summary.Hardware.CpuMhz -PassThru | `
  Add-Member -Type NoteProperty -Name "Host Product Name" -Value $VMHost.Summary.Config.Product.Name -PassThru | `
  Add-Member -Type NoteProperty -Name "Host Product Version" -Value $VMHost.Summary.Config.Product.Version -PassThru | `
  Add-Member -Type NoteProperty -Name "Host Product Build" -Value $VMHost.Summary.Config.Product.Build -PassThru | `
  Add-Member -Type NoteProperty -Name "Host Service Console" -Value $VMHost.Config.Network.ConsolevNic[0].Spec.IP.IPAddress -PassThru | `
  Add-Member -Type NoteProperty -Name "Host Service Console Subnet Mask" -Value $VMHost.Config.Network.ConsolevNic[0].Spec.IP.SubnetMask -PassThru | `
  Add-Member -Type NoteProperty -Name "Host Service Console 1" -Value $VMHost.Config.Network.ConsolevNic[1].Spec.IP.IPAddress -PassThru | `
  Add-Member -Type NoteProperty -Name "Host Service Console 1 Subnet Mask" -Value $VMHost.Config.Network.ConsolevNic[1].Spec.IP.SubnetMask -PassThru | `
  Add-Member -Type NoteProperty -Name "Host vMotion IP Address" -Value $VMHost.Config.vMotion.IPConfig.IpAddress -PassThru | `
  Add-Member -Type NoteProperty -Name "Host vMotion Subnet Mask" -Value $VMHost.Config.vMotion.IPConfig.SubnetMask

However it could be written much cleaner as:

$row = New-Object PSObject -Property  @{
  "Host Name" = $VMHost.Summary.Config.Name.ToUpper()
  "# of Sessions on Host" = $VMHost.vm.Count
  "Host is Member of Cluster" = (Get-Cluster -VMHost $ESXHost).Name.ToUpper()
  "Host Vendor" = $VMHost.Hardware.SystemInfo.Vendor
  "Host Model" = $VMHost.Hardware.SystemInfo.Model
  "Host RAM" = [math]::Round(($VMHost.Summary.Hardware.MemorySize) / 1GB)
  "Host CPU Model" = $VMHost.Summary.Hardware.CpuModel
  "Host CPU Count" = $VMHost.Summary.Hardware.NumCpuThreads
  "Host CPU Speed" = $VMHost.Summary.Hardware.CpuMhz
  "Host Product Name" = $VMHost.Summary.Config.Product.Name
  "Host Product Version" = $VMHost.Summary.Config.Product.Version
  "Host Product Build" = $VMHost.Summary.Config.Product.Build
  "Host Service Console" = $VMHost.Config.Network.ConsolevNic[0].Spec.IP.IPAddress
  "Host Service Console Subnet Mask" = $VMHost.Config.Network.ConsolevNic[0].Spec.IP.SubnetMask
  "Host Service Console 1" = $VMHost.Config.Network.ConsolevNic[1].Spec.IP.IPAddress
  "Host Service Console 1 Subnet Mask" = $VMHost.Config.Network.ConsolevNic[1].Spec.IP.SubnetMask
  "Host vMotion IP Address" = $VMHost.Config.vMotion.IPConfig.IpAddress
  "Host vMotion Subnet Mask" = $VMHost.Config.vMotion.IPConfig.SubnetMask
}

Regards, Robert

Blog: https://rvdnieuwendijk.com/ | Twitter: @rvdnieuwendijk | Author of: https://www.packtpub.com/virtualization-and-cloud/learning-powercli-second-edition
0 Kudos
LucD
Leadership
Leadership
Jump to solution

Just tried it again and I seem to have all the properties present and populated.

Did you perhaps miss a curly brace somewhere ?

Could you attach the script you are using ?


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

0 Kudos
LucD
Leadership
Leadership
Jump to solution

I don't see why piping the Add-Member cmdlets together would make a difference ?

Cleaner ? Perhaps yes, but the properties are in a random order 😞


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

0 Kudos
RvdNieuwendijk
Leadership
Leadership
Jump to solution

You are right about both things Luc. I should have tested the original code before I wrote my post. :smileyblush:

The random order of the properties after using "New-Object PSObject -Property @{}" is something I don't like either. They really should fix this in PowerShell v3.

Blog: https://rvdnieuwendijk.com/ | Twitter: @rvdnieuwendijk | Author of: https://www.packtpub.com/virtualization-and-cloud/learning-powercli-second-edition
0 Kudos
LucD
Leadership
Leadership
Jump to solution

Fyi I raised that 'random order' thing at the PS deepdive, the answer is that PS doesn't garantue any order of properties in an object.

In a PS script there are 3 steps; input gathering, processing, output production.

The answer is to use a Select-Object, during the outout phase, if you want to have the properties in a specific order 😞

You can imagine that I don't really find this an acceptable answer.

If PowerShell would be a programming language, I would say yes, but PS is a scripting language for administrators, so I'm not buying that answer 🙂


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

0 Kudos
RvdNieuwendijk
Leadership
Leadership
Jump to solution

I understand what Microsoft is saying about the order of properties in objects, but I still don't like it either. Can't we raise a petition? Smiley Wink

Blog: https://rvdnieuwendijk.com/ | Twitter: @rvdnieuwendijk | Author of: https://www.packtpub.com/virtualization-and-cloud/learning-powercli-second-edition
0 Kudos
LucD
Leadership
Leadership
Jump to solution

You can open a ticket/bug report on Connect and if it gets enough votes, it will be handled.

But I fear they will stick with official explanation I already got 😞


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

0 Kudos