VMware Cloud Community
VJ_VMware_111
Enthusiast
Enthusiast
Jump to solution

How to make this powercli Script that i created to run faster than now?

Hello VMware and Powershell Experts,

I have created the below powercli script to collect inventory of our 5.5 and 6.0 VMware Hosts, VMs and Clusters. The problem with it is, it is terribly slow. It takes half a day to run this script fully to get details from 3 to 4 of our vCenters. Is there a way to make this script faster, by reducing the processing times, and cutting down unwanted loops, while maintaining the exact output, as requested by my manager? Input vcenter information at line 3 of the script. Thanks a million in advance!

$Username = Read-Host -Prompt "Please enter your Username:"

$Password = Read-Host -Prompt "Please enter your Password:"

$vcenters = @("vcenter1","vcenter2","vcenter3")

$GB = 1073718240

$HostReport = @()

$VMReport = @()

$ClusterReport = @()

ForEach ($vcenter in $vcenters)

{

Connect-VIServer $vcenter -User $Username -Password $Password

Get-VMHost |Get-View |%{ 

     $HReport = "" | select vCenter, Hostname, Cluster, Stand_Alone, State, ESX_Version, Build, Model, CPU_Sockets, CPU_Cores, RAM_GB, VM

     $HReport.vCenter = $vcenter

  $HReport.Hostname = $_.Name

  $HReport.Cluster = Get-Cluster -VMHost $_.Name

  $HReport.Stand_Alone = if($HReport.Cluster) {'No'} else {'Yes'}

     $HReport.State = $_.runtime.connectionState

     $HReport.ESX_Version =$_.config.product.version

     $HReport.Build =$_.config.product.build

     $HReport.Model =$_.hardware.systemInfo.model

     $HReport.CPU_Sockets =$_.hardware.CpuInfo.numCpuPackages

     $HReport.CPU_Cores =$_.hardware.CpuInfo.numCpuCores

     $HReport.RAM_GB = [math]::Round((($_.hardware.memorySize)/($GB)),1)

  $HReport.VM = $_.vm.Count

     $HostReport += $HReport

}

Get-VM |Get-View |%{

     $VReport = "" | select vCenter, VM, IP, OS, ESX_Host, Status

  $VReport.vCenter = $vcenter

  $VReport.VM = $_.config.Name

  $VReport.IP = $_.guest.ipaddress

  $VReport.OS = $_.config.guestFullName

  $VReport.ESX_Host = Get-VMHost -VM $_.config.Name

  $VReport.Status = $_.guest.guestState

  $VMReport += $VReport

}

Get-Cluster |Get-View |%{

  $esx = Get-VMHost -Location $_.Name

  $vm = Get-VM -Location $_.Name

  $CReport = "" | select vCenter, DC, Cluster, Number_of_Hosts, Number_of_VMs, Total_Processors, Total_Cores, Total_Physical_Memory_GB, Configured_Memory_GB, Available_Memory_GB, Total_CPU_Ghz, Configured_CPU_Ghz, Available_CPU_Ghz  

  $CReport.vCenter = $vcenter

  $CReport.DC = Get-Datacenter -Cluster $_.Name

  $CReport.Number_of_Hosts = $esx.Count

  $CReport.Number_of_VMs = $vm.Count

    $CReport.Cluster = $_.Name

    $CReport.Total_Processors = ($esx | measure -InputObject {$_.Extensiondata.Summary.Hardware.NumCpuPkgs} -Sum).Sum

    $CReport.Total_Cores = ($esx | measure -InputObject {$_.Extensiondata.Summary.Hardware.NumCpuCores} -Sum).Sum

    $CReport.Total_Physical_Memory_GB = [math]::Round((((($esx | Measure-Object -Property MemoryTotalMB -Sum).Sum)/1024)/($GB)),1)

    $CReport.Configured_Memory_GB = [math]::Round((((($esx | Measure-Object -Property MemoryUsageMB -Sum).Sum)/1024)/($GB)),1)

    $CReport.Available_Memory_GB = [math]::Round((((($esx | Measure-Object -InputObject {$_.MemoryTotalMB - $_.MemoryUsageMB} -Sum).Sum)/1024)/($GB)),1)

    $CReport.Total_CPU_Ghz = [math]::Round((((($esx | Measure-Object -Property CpuTotalMhz -Sum).Sum)/1024)/($GB)),1)

    $CReport.Configured_CPU_Ghz = [math]::Round((((($esx | Measure-Object -Property CpuUsageMhz -Sum).Sum)/1024)/($GB)),1)

    $CReport.Available_CPU_Ghz = [math]::Round((((($esx | Measure-Object -InputObject {$_.CpuTotalMhz - $_.CpuUsageMhz} -Sum).Sum)/1024)/($GB)),1)

  $ClusterReport += $CReport

  $esx = ""

  $vm = ""

  }

Disconnect-VIServer $vcenter -Confirm:$false

}

$HostReport | Export-Csv "C:\HostReport.csv" -NoTypeInformation

$VMReport | Export-Csv "C:\VMReport.csv" -NoTypeInformation

$ClusterReport | Export-Csv "C:\ClusterReport.csv" -NoTypeInformation

1 Solution

Accepted Solutions
LucD
Leadership
Leadership
Jump to solution

Try like this

$Username = Read-Host -Prompt "Please enter your Username:"

$Password = Read-Host -Prompt "Please enter your Password:"

$vcenters = @("vcenter1","vcenter2","vcenter3")

$HostReport = @()

$VMReport = @()

$ClusterReport = @()

ForEach ($vcenter in $vcenters)

{

    Connect-VIServer $vcenter -User $Username -Password $Password

    Get-View -ViewType HostSystem -Property Name,Parent,Runtime.ConnectionState,Config.Product,Hardware,VM -Server $vcenter | %{

        $obj = [ordered]@{

            vCenter = $vcenter

            Hostname = $_.Name

            Cluster = &{

                $parent = Get-View -Id $_.Parent -Property Name,Parent

                while($parent -isnot [VMware.Vim.ClusterComputeResource] -and $parent -isnot [VMware.Vim.Datacenter]){

                    $parent = Get-View -Id $parent.Parent -Property Name,Parent

                }

                if($parent -is [VMware.Vim.Datacenter]){

                    ''

                    $script:sa = 'Yes'

                }

                else{

                    $parent.Name

                    $script:sa = 'No'

                }

            }

            Stand_Alone = $script:sa

            State = $_.runtime.connectionState

            ESX_Version =$_.config.product.version

            Build =$_.config.product.build

            Model =$_.hardware.systemInfo.model

            CPU_Sockets =$_.hardware.CpuInfo.numCpuPackages

            CPU_Cores =$_.hardware.CpuInfo.numCpuCores

            RAM_GB = [math]::Round((($_.hardware.memorySize)/(1GB)),1)

            VM = $_.vm.Count

        }

        $HostReport += New-Object PSObject -Property $obj

    }

    Get-View -ViewType VirtualMachine -Property Name,Config,Guest,Runtime.Host -Server $vcenter | %{

        $obj = [ordered]@{

            vCenter = $vcenter

            VM = $_.config.Name

            IP = $_.guest.ipaddress

            OS = $_.config.guestFullName

            ESX_Host = (Get-View -Id $_.Runtime.Host -Property Name).Name

            Status = $_.guest.guestState

        }

        $VMReport += New-Object PSObject -Property $obj

    }

    Get-View -ViewType ClusterComputeResource -Property Name,Parent,Host -Server $vcenter | %{

        $esx = Get-View -Id $_.Host -Property VM,Hardware,Summary.QuickStats -Server $vcenter

        $obj = [ordered]@{

            vCenter = $vcenter

            DC = &{

                $parent = Get-View -Id $_.Parent -Property Name,Parent

                while($parent -isnot [VMware.Vim.Datacenter]){

                    $parent = Get-View -Id $parent.Parent -Property Name,Parent

                }

                $parent.Name

            }

            Number_of_Hosts = $_.Host.Count

            Number_of_VMs = $esx.VM.Count

            Cluster = $_.Name

            Total_Processors = ($esx.Hardware.CpuInfo.NumCpuPackages | Measure-Object -Sum).Sum

            Total_Cores = ($esx.Hardware.CpuInfo.NumCpuCores | Measure-Object -Sum).Sum

            Total_Physical_Memory_GB = &{

                $script:pmem = [math]::Round(($esx.Hardware.MemorySize | Measure-Object -Sum ).Sum/1GB,1)

                $script:pmem

            }

            Configured_Memory_GB = &{

                $script:umem = [math]::Round(($esx.Summary.QuickStats.OverallMemoryUsage | Measure-Object -Sum).Sum/1GB,1)

                $script:umem

            }

            Available_Memory_GB = $script:pmem - $script:umem

            Total_CPU_Ghz = $ucpu = &{

                $script:pcpu = [math]::Round(($esx.Hardware.CpuInfo.Hz | Measure-Object -Sum).Sum/10E5,1)

                $script:pcpu

            }

            Configured_CPU_Ghz = &{

                $script:ucpu = [math]::Round(($esx.Summary.QuickStats.OverallCpuUsage | Measure-Object -Sum).Sum/10E5,1)

                $script:ucpu

            }

            Available_CPU_Ghz = $script:pcpu - $script:ucpu

        }

        $ClusterReport += New-Object psobject -Property $obj

    }

    Disconnect-VIServer $vcenter -Confirm:$false

}

$HostReport | Export-Csv "C:\HostReport.csv" -NoTypeInformation -UseCulture

$VMReport | Export-Csv "C:\VMReport.csv" -NoTypeInformation -UseCulture

$ClusterReport | Export-Csv "C:\ClusterReport.csv" -NoTypeInformation -UseCulture


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

View solution in original post

19 Replies
kwhornlcs
Enthusiast
Enthusiast
Jump to solution

The script can definitely be optimized a bit, lots of looping and getting information you already have. Let me test a rewrite. Back soon.

LucD
Leadership
Leadership
Jump to solution

Try like this

$Username = Read-Host -Prompt "Please enter your Username:"

$Password = Read-Host -Prompt "Please enter your Password:"

$vcenters = @("vcenter1","vcenter2","vcenter3")

$HostReport = @()

$VMReport = @()

$ClusterReport = @()

ForEach ($vcenter in $vcenters)

{

    Connect-VIServer $vcenter -User $Username -Password $Password

    Get-View -ViewType HostSystem -Property Name,Parent,Runtime.ConnectionState,Config.Product,Hardware,VM -Server $vcenter | %{

        $obj = [ordered]@{

            vCenter = $vcenter

            Hostname = $_.Name

            Cluster = &{

                $parent = Get-View -Id $_.Parent -Property Name,Parent

                while($parent -isnot [VMware.Vim.ClusterComputeResource] -and $parent -isnot [VMware.Vim.Datacenter]){

                    $parent = Get-View -Id $parent.Parent -Property Name,Parent

                }

                if($parent -is [VMware.Vim.Datacenter]){

                    ''

                    $script:sa = 'Yes'

                }

                else{

                    $parent.Name

                    $script:sa = 'No'

                }

            }

            Stand_Alone = $script:sa

            State = $_.runtime.connectionState

            ESX_Version =$_.config.product.version

            Build =$_.config.product.build

            Model =$_.hardware.systemInfo.model

            CPU_Sockets =$_.hardware.CpuInfo.numCpuPackages

            CPU_Cores =$_.hardware.CpuInfo.numCpuCores

            RAM_GB = [math]::Round((($_.hardware.memorySize)/(1GB)),1)

            VM = $_.vm.Count

        }

        $HostReport += New-Object PSObject -Property $obj

    }

    Get-View -ViewType VirtualMachine -Property Name,Config,Guest,Runtime.Host -Server $vcenter | %{

        $obj = [ordered]@{

            vCenter = $vcenter

            VM = $_.config.Name

            IP = $_.guest.ipaddress

            OS = $_.config.guestFullName

            ESX_Host = (Get-View -Id $_.Runtime.Host -Property Name).Name

            Status = $_.guest.guestState

        }

        $VMReport += New-Object PSObject -Property $obj

    }

    Get-View -ViewType ClusterComputeResource -Property Name,Parent,Host -Server $vcenter | %{

        $esx = Get-View -Id $_.Host -Property VM,Hardware,Summary.QuickStats -Server $vcenter

        $obj = [ordered]@{

            vCenter = $vcenter

            DC = &{

                $parent = Get-View -Id $_.Parent -Property Name,Parent

                while($parent -isnot [VMware.Vim.Datacenter]){

                    $parent = Get-View -Id $parent.Parent -Property Name,Parent

                }

                $parent.Name

            }

            Number_of_Hosts = $_.Host.Count

            Number_of_VMs = $esx.VM.Count

            Cluster = $_.Name

            Total_Processors = ($esx.Hardware.CpuInfo.NumCpuPackages | Measure-Object -Sum).Sum

            Total_Cores = ($esx.Hardware.CpuInfo.NumCpuCores | Measure-Object -Sum).Sum

            Total_Physical_Memory_GB = &{

                $script:pmem = [math]::Round(($esx.Hardware.MemorySize | Measure-Object -Sum ).Sum/1GB,1)

                $script:pmem

            }

            Configured_Memory_GB = &{

                $script:umem = [math]::Round(($esx.Summary.QuickStats.OverallMemoryUsage | Measure-Object -Sum).Sum/1GB,1)

                $script:umem

            }

            Available_Memory_GB = $script:pmem - $script:umem

            Total_CPU_Ghz = $ucpu = &{

                $script:pcpu = [math]::Round(($esx.Hardware.CpuInfo.Hz | Measure-Object -Sum).Sum/10E5,1)

                $script:pcpu

            }

            Configured_CPU_Ghz = &{

                $script:ucpu = [math]::Round(($esx.Summary.QuickStats.OverallCpuUsage | Measure-Object -Sum).Sum/10E5,1)

                $script:ucpu

            }

            Available_CPU_Ghz = $script:pcpu - $script:ucpu

        }

        $ClusterReport += New-Object psobject -Property $obj

    }

    Disconnect-VIServer $vcenter -Confirm:$false

}

$HostReport | Export-Csv "C:\HostReport.csv" -NoTypeInformation -UseCulture

$VMReport | Export-Csv "C:\VMReport.csv" -NoTypeInformation -UseCulture

$ClusterReport | Export-Csv "C:\ClusterReport.csv" -NoTypeInformation -UseCulture


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

VJ_VMware_111
Enthusiast
Enthusiast
Jump to solution

Thanks LucD a lot! This script is excellent and it cut down the processing time by more than 50 times. Awesome! Thanks again.

I have only one more thing needed still. In the cluster report, it is giving configured memory as 0, while in web-client i am seeing more usage. Please help here to get cluster cpu and memory total capacity, used capacity, and available capacity. Thanks in advance!

0 Kudos
LucD
Leadership
Leadership
Jump to solution

There was also an error in the CPU calculation.

This is the updated cluster part.

    Get-View -ViewType ClusterComputeResource -Property Name,Parent,Host | %{

        $esx = Get-View -Id $_.Host -Property VM,Hardware,Summary.QuickStats

        $obj = [ordered]@{

            vCenter = $vcenter

            DC = &{

                $parent = Get-View -Id $_.Parent -Property Name,Parent

                while($parent -isnot [VMware.Vim.Datacenter]){

                    $parent = Get-View -Id $parent.Parent -Property Name,Parent

                }

                $parent.Name

            }

            Number_of_Hosts = $_.Host.Count

            Number_of_VMs = $esx.VM.Count

            Cluster = $_.Name

            Total_Processors = ($esx.Hardware.CpuInfo.NumCpuPackages | Measure-Object -Sum).Sum

            Total_Cores = ($esx.Hardware.CpuInfo.NumCpuCores | Measure-Object -Sum).Sum

            Total_Physical_Memory_GB = &{

                $script:pmem = [math]::Round(($esx.Hardware.MemorySize | Measure-Object -Sum ).Sum/1GB,1)

                $script:pmem

            }

            Configured_Memory_GB = &{

                $script:umem = [math]::Round(($esx.Summary.QuickStats.OverallMemoryUsage | Measure-Object -Sum).Sum/1KB,1)

                $script:umem

            }

            Available_Memory_GB = $script:pmem - $script:umem

            Total_CPU_Ghz = &{

                $script:pcpu = [math]::Round(($esx | Measure-Object -InputObject {$_.Hardware.CpuInfo.Hz * $_.Hardware.CpuInfo.numCpuCores} -Sum).Sum/1E9,1)

                $script:pcpu

            }

            Configured_CPU_Ghz = &{

                $script:ucpu = [math]::Round(($esx.Summary.QuickStats.OverallCpuUsage | Measure-Object -Sum).Sum/1E3,1)

                $script:ucpu

            }

            Available_CPU_Ghz = $script:pcpu - $script:ucpu

        }

        $ClusterReport += New-Object psobject -Property $obj

    }


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

kwhornlcs
Enthusiast
Enthusiast
Jump to solution

Damn, was almost there (good thing I didn't spend a few hours rewriting or anything Smiley Happy) Unfortunately hitched my wagons to get-vmhost and get-vm in the first two sections to get things like hostname and memory GB without calculating, but LucD's script is 51% (3 minutes on 500VM) faster in the VM section where most of the time is spent.

Question to LucD,  (and proving there's many different ways to get to the same answer) is there a preference or advantage to creating the Hash then adding it to the Object in the ForEach loop rather than getting everything at once with a select-property and calculated fields, for instance I modified what I was working on using the Get-View calculations as follows and got pretty much identical measured run times:

$VMReport += Get-View -ViewType VirtualMachine -Property Name,Config,Guest,Runtime.Host -Server $vcenter | Select @{N='vcenter';e={$vcenter}},`

  @{N='VM';E={$_.Name}},@{N='IP';E={$_.guest.ipaddress}},@{N='OS';E={$_.Guest.guestFullName}},`

  @{N='ESX_Host';E={(Get-View -Id $_.Runtime.Host -Property Name).Name}},`

  @{N='State';E={$_.Guest.guestState}}

0 Kudos
LucD
Leadership
Leadership
Jump to solution

No, I don't think there is any performance difference between the two ways of doing that.

For readability I prefer the method I used.

One tends to forget where the object goes at the end of a longer code block :smileycool:

But I have to admit I also use the method you showed.

It tends to be more in line with the coding train of thought at occasions, and it saves on an array declaration.


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

0 Kudos
VJ_VMware_111
Enthusiast
Enthusiast
Jump to solution

It works perfect! Thanks again LucD. Smiley Happy

0 Kudos
VJ_VMware_111
Enthusiast
Enthusiast
Jump to solution

Hi LucD,

The below error is going into an endless loop, for more than an hour, when i run the script. Can you please check?

Get-View : Cannot validate argument on parameter 'Id'. The argument is null, empty, or an element of the argument collection contains a null value. Supply a collection that does

not contain any null values and then try the command again.

At C:\Users\vj\Desktop\Desktop 1\My Scripts\LucD.ps1:18 char:44

+                     $parent = Get-View -Id $parent.Parent -Property Name,Parent

+                                            ~~~~~~~~~~~~~~

    + CategoryInfo          : InvalidData: (:) [Get-View], ParameterBindingValidationException

    + FullyQualifiedErrorId : ParameterArgumentValidationError,VMware.VimAutomation.ViCore.Cmdlets.Commands.DotNetInterop.GetVIView

0 Kudos
LucD
Leadership
Leadership
Jump to solution

That line is used multiple times. In which context do you get the error?


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

0 Kudos
VJ_VMware_111
Enthusiast
Enthusiast
Jump to solution

Its on the host part, to get the cluster name. I have underlined it.

ForEach ($vcenter in $vcenters)

{

    Connect-VIServer $vcenter -User $Username -Password $Password

    Get-View -ViewType HostSystem -Property Name,Parent,Runtime.ConnectionState,Config.Product,Hardware,VM -Server$vcenter | %{

        $obj = [ordered]@{

            vCenter = $vcenter

            Hostname = $_.Name

            Cluster = &{

                $parent = Get-View -Id $_.Parent -Property Name,Parent

                while($parent -isnot [VMware.Vim.ClusterComputeResource] -and $parent -isnot [VMware.Vim.Datacenter]){

                    $parent = Get-View -Id $parent.Parent -Property Name,Parent

                }

                if($parent -is [VMware.Vim.Datacenter]){

                    ''

                    $script:sa = 'Yes'

                }

                else{

                    $parent.Name

                    $script:sa = 'No'

                }

            }

0 Kudos
LucD
Leadership
Leadership
Jump to solution

Are you perhaps connected directly to an ESXi node instead of a vCenter?


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

0 Kudos
VJ_VMware_111
Enthusiast
Enthusiast
Jump to solution

It works now without any errors. I did not had to change anything. Smiley Happy

0 Kudos
LucD
Leadership
Leadership
Jump to solution

Perhaps something got left behind from a previous run.
Next time you see the issue, try stopping/starting your PowerCLI session.


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

VJ_VMware_111
Enthusiast
Enthusiast
Jump to solution

Thanks LucD. Is it possible to get tag information of hosts without slowing down the script?

0 Kudos
LucD
Leadership
Leadership
Jump to solution

Not really, the Tag related cmdlets do not use the API methods.
They interface directly with the Web Client.

And these interfaces are not so public that it would be easy to write the functionality such that it executes faster.


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

0 Kudos
VJ_VMware_111
Enthusiast
Enthusiast
Jump to solution

Thanks LucD..

0 Kudos
chorgeas
Hot Shot
Hot Shot
Jump to solution

This is very helpful program. Need to know, if I want to get ESXi server configuration information then how can I do it?

Example: Below is the vSphere command line to check RX value. Can I get this information using powercli? 

To check RX descriptor in ESXi host

  1. Connect to ESXi host CLI
  2. Execute command ethtool -g <vmnicX>
  3. From the command output verify Current Rx value is equal to 2048
0 Kudos
LucD
Leadership
Leadership
Jump to solution

Does this show the values you are after?

foreach($esx in Get-VMHost){

    $esxcli = Get-EsxCli -VMHost $esx -V2

    $esxcli.network.nic.coalesce.get.Invoke() |

    Select @{N='VMHost';E={$esx.Name}},*

}


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

0 Kudos
xjjc004
Contributor
Contributor
Jump to solution

How do I filter out Red Hat VMs?  I need to generate a report that only has RHEL servers.

0 Kudos