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
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
The script can definitely be optimized a bit, lots of looping and getting information you already have. Let me test a rewrite. Back soon.
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
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!
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
Damn, was almost there (good thing I didn't spend a few hours rewriting or anything ) 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}}
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
It works perfect! Thanks again LucD.
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
That line is used multiple times. In which context do you get the error?
Blog: lucd.info Twitter: @LucD22 Co-author PowerCLI Reference
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'
}
}
Are you perhaps connected directly to an ESXi node instead of a vCenter?
Blog: lucd.info Twitter: @LucD22 Co-author PowerCLI Reference
It works now without any errors. I did not had to change anything.
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
Thanks LucD. Is it possible to get tag information of hosts without slowing down the script?
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
Thanks LucD..
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
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
How do I filter out Red Hat VMs? I need to generate a report that only has RHEL servers.