VMware Cloud Community
swamynaveen
Enthusiast
Enthusiast

Best PowerCLi Script to fetch bulk VM and VMhost Inventory Report on Daily basis.

Hi All, I'm using the following script to fetch VM/VMhost inventory for bulk numbers like 50000+ VMs & 12000+Hosts. but the script is taking many days. Hence I would like to know is there a way to improve the speed of the report. Kindly advise.

 

 

$VC1 =$global:DefaultVIServers.count
$CurrentDate = Get-Date -Format 'MM-dd-yyyy_hh-mm-ss'
$filename ="D:\Reports\VM_VMHost_Inventory_Consolidated-on-$($CurrentDate).xlsx"
#$nameToExclude ="vLCS"
$report_VMs1 = foreach($vc in $global:defaultviservers){
    foreach($vm in (Get-View -ViewType VirtualMachine -Filter @{“Runtime.PowerState”=”poweredOn”} -Property Name,runtime.powerState,runtime.consolidationNeeded,Guest.net,Config.Hardware.numCPU,Config.Hardware.MemoryMB,
    Runtime.Host,Guest.GuestFullName, Config.GuestFullName,Parent,ResourcePool,Config.Hardware.Device,Config.version,Config.Tools.ToolsVersion,guest.toolsversionstatus,
    Config.Files.VMPathName,Config.Template -Server $vc )){
        if($vm.Config.Template){
            $t = Get-View -Id (Get-View $vm.Runtime.Host).Parent
        }
        else{
            $t = Get-View $vm.ResourcePool -Property Name,Parent -Server $vc
            while($t.getType().Name -eq "ResourcePool"){
              $t = Get-View $t.Parent -Property Name,Parent -Server $vc
            }
        }
        if($t.GetType().Name -eq "ClusterComputeResource"){
            $cluster = $t.Name
        }
        else{
            $cluster = "Stand Alone Host"
        }
        while($t.getType().Name -ne "Datacenter"){
            $t = Get-View $t.Parent -Property Name,Parent -Server $vc
        }
        $datacenter = $t.Name
    
        #$vm.Config.Hardware.Device | where {$_ -is [VMware.Vim.VirtualDisk]}  
        $vm |Select @{N='vCenter';E={$vc.Name}},@{N="Datacenter";E={$datacenter}},@{N="Cluster";E={$cluster}},
        @{N="VM";E={$vm.Name}},
        @{N='powerState';E={$vm.runtime.powerState}},
        @{N='IP';E={[string]::Join(',',($vm.Guest.Net | %{$_.IpAddress | where{$_.Split('.').Count -eq 4} | %{$_}}))}},
        @{N='NumCPU';E={$vm.config.Hardware.NumCpu}},
        @{N='Memory GB';E={$vm.Config.Hardware.MemoryMB| %{[math]::Round($_/1kb,2)}}},
        @{N="TotalHDD"; E={($vm.Config.Hardware.Device | where{$_ -is [VMware.Vim.VirtualDisk]}).Count}},
        #@{N="ProvisionSpaceGB";E={$vm.Summary.Storage.UnCommitted| %{[math]::Round($_/1GB,2)}}},
        #@{N="UsedSpaceGB";E={$vm.Summary.Storage.Committed| %{[math]::Round($_/1GB,2)}}},
        @{N='VMHost';E={$script:esx = Get-View -Id $vm.Runtime.Host -Server $vc ; $script:esx.name}},
        @{N='VMHost_Version';E={$script:esx = Get-View -Id $vm.Runtime.Host -Server $vc ; (Get-VMHost $Script:esx.name).ExtensionData.Config.Product.FullName }},
        @{N='VMHost_Make';E={$script:esx = Get-View -Id $vm.Runtime.Host -Server $vc ; Get-VMHost $Script:esx.name | select -ExpandProperty Manufacturer}},
        @{N='VMHost_Model';E={$script:esx = Get-View -Id $vm.Runtime.Host -Server $vc ; Get-VMHost $Script:esx.name | select -ExpandProperty Model}},
        @{N='VMHost_SerialNo';E={$script:esx = Get-View -Id $vm.Runtime.Host -Server $vc ; $script:esxcli = Get-EsxCli -VMHost $Script:esx.name; $script:esxcli.hardware.platform.get().SerialNumber}},
        @{N='GuestOS';E={$vm.Guest.GuestFullName}},
        @{N='ConfiguredOS';E={$vm.Config.GuestFullName}},
        @{N="VMware Tools version";E={$_.Config.Tools.ToolsVersion}},
        #@{N='DNSHostname';E={$_.guest.hostname}},
        @{N='VMConfigFile';E={$vm.config.files.VMpathname}}
        #@{N='PGName';E={(Get-View -Id $_.Network -Property Name).Name -join '|'}}
        
        

        }}

        $report_VMs1 |Export-Excel -Path $filename -WorksheetName "VM_Inventory" -AutoSize -FreezeTopRow -ClearSheet -TableName "VM_Inventory" #-TableStyle Light12


$report_Hosts2 = foreach($vc in $global:defaultviservers){
                 foreach ($vmhost in Get-VMHost -State Connected,Maintenance -Server $vc){

        Get-VMHost $vmhost | Select Name,PowerState,State, IsStandalone,@{N='Product';E={$_.ExtensionData.Config.Product.FullName}},@{N=“Memory GB“;E={“” + [math]::round(($_| get-view).Hardware.MemorySize / 1GB, 0) }},@{N="CPU Sockets";E={($_| Get-View).Hardware.CpuInfo.NumCpuPackages}},
    @{N="PCores";E={($_| Get-View).Hardware.CpuInfo.NumCpuCores}},LicenseKey,#@{N="ManagementIP"; E={Get-VMHostNetworkAdapter -VMHost $_ -VMKernel | ?{$_.ManagementTrafficEnabled} | %{$_.Ip}}},LicenseKey, #@{N='LicenseName';E={$vmhostLM.AssignedLicense.Name | Select-Object -Unique}},@{N='ExpirationDate';E={$_.AssignedLicense.Properties.where{$_.Key -eq 'expirationDate'}.Value }},
@{N='vCenter';E={$_.Uid.Split('@')[1].Split(':')[0]}},
@{N="Cluster Name";E={($_ | Get-Cluster).Name}},@{N=”Datacenter”;E={Get-Datacenter -VMHost $_}},
Manufacturer, Model, ProcessorType,@{N='Serial';E={
$script:esxcli = Get-EsxCli -VMHost $_.Name

$script:esxcli.hardware.platform.get().SerialNumber }}

}} 
$report_Hosts2 | Export-Excel -Path $filename -WorksheetName "VMHost_Inventory" -AutoSize -FreezeTopRow -ClearSheet -TableName "VMHost_Inventory" #-TableStyle Light13


#$fromaddress = "XXXX"
$fromaddress = "XXXX"
$toaddress = "XXXX"


$Subject = "VM/VHost Inventory for vCenters: $VC1 ."

$attachment = $filename
#$attachment1 = $filename1

$smtpserver = "XXXX"

$message = new-object System.Net.Mail.MailMessage
$message.From = $fromaddress
$message.To.Add($toaddress)
#$message.To.Add($toaddress1)
#$message.To.Add($toaddress2)
#$message.cc.Add($CCaddress)
#$message.cc.Add($CCaddress1)
#$message.cc.Add($CCaddress2)
#$message.IsBodyHtml = $True
$message.Subject = $Subject
$attach = new-object Net.Mail.Attachment($attachment)
$message.Attachments.Add($attach)
#$attach1 = new-object Net.Mail.Attachment($attachment1)
#$message.Attachments.Add($attach1)

$message.body = $body
$smtp = new-object Net.Mail.SmtpClient($smtpserver)

$MailText = @"
Hi,

PFA the VM/VMhost inventory report for $VC1 vCenters.

$global:DefaultVIServers | select Name   



===================================
Report generated on:- $CurrentDate.

===================================
 
"@
 
   $message.Body = $MailText
   $smtp.Send($message)

 

 

 

 

Regards,

Naveen

Reply
0 Kudos
7 Replies
LucD
Leadership
Leadership

Instead of running 1 script against all vCenters, perhaps try running individual background jobs (Start-Job) for each vCenter.

When you are using PSv7, you might also have a look at which foreach loops could use the Parallel switch.


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

Reply
0 Kudos
swamynaveen
Enthusiast
Enthusiast

@LucD Thanks for the update. Can you provide one example with my existing script?  I am using Powershell 5.x in my current reporting server. Do you suggest me to use PSv7 to acheive this requirement?

 

Name Value
---- -----
PSVersion 5.0.10586.117
PSCompatibleVersions {1.0, 2.0, 3.0, 4.0...}
BuildVersion 10.0.10586.117
CLRVersion 4.0.30319.42000
WSManStackVersion 3.0
PSRemotingProtocolVersion 2.3
SerializationVersion 1.1.0.1

Reply
0 Kudos
LucD
Leadership
Leadership

You can try with the Start-Job option.
Have a look at Solved: Re: Need help running start-job within loop... - VMware Technology Network VMTN which shows the basics on how to use that.
How to use the Connect-VIServer with SessionId is shown in Solved: Re: Is there a way to connect to multiple VMware s... - VMware Technology Network VMTN


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

Reply
0 Kudos
swamynaveen
Enthusiast
Enthusiast

@LucD Thank you sharing the examples. I have incorporated VM & Host report script wthin start-job options. Below is the code.  but with this code i'm getting multiple output files. How can we merge all the output files into one consolidation Excel file and send it over an email?  Also, few details are missing in the VM report like VM ProvisionedSpaceGB,UsedSpaceGB,DNSHostname &Portgroupname when i use get-view option. Pls advise on them.

 

$vcNames = Get-Content "C:\VCList2.txt"

if($global:DefaultVIServers){
Write-Host "Disconnected to below vCenter servers: $global:DefaultVIServers " -BackgroundColor Cyan

    Disconnect-VIServer -Server $global:DefaultVIServers.Name -Confirm:$false

}

$code = {

param($vc)

$uname = "XXXX"
$Pswd = Get-Content "D:\cred\Cred.txt" | ConvertTo-SecureString
$Creds = New-Object System.Management.Automation.PsCredential($uname, $Pswd)
$CurrentDate = Get-Date -Format 'MM-dd-yyyy_hh-mm-ss'
$filename ="G:Reports\VMHost_Inventory-$($vc)-Consolidated-on-$($CurrentDate).xlsx"
Set-PowerCLIConfiguration -DisplayDeprecationWarnings $false -Confirm:$false | Out-Null
Write-Host "Connecting to vCenter server: $vc " -BackgroundColor DarkBlue
$VIConnection = Connect-VIServer -Server $vc -Credential $Creds | %{"$($_.Name),$($_.SessionId)"}

If($? -Eq $True){
	Write-Host "Connected to: $vc" -Foregroundcolor "Green" 
	}
Else{
			  Write-Host "Error in Connecting to the vcenter: $vc ; Check the issue based on Error message" -Foregroundcolor "Red" 
		}

$report_VMs1=foreach($vm in (Get-View -ViewType VirtualMachine -Filter @{“Runtime.PowerState”=”poweredOn”} -Property Name,runtime.powerState,runtime.consolidationNeeded,Guest.net,Config.Hardware.numCPU,Config.Hardware.MemoryMB,
    Runtime.Host,Guest.GuestFullName, Config.GuestFullName,Parent,ResourcePool,Config.Hardware.Device,Config.version,Config.Tools.ToolsVersion,guest.toolsversionstatus,
    Config.Files.VMPathName,Config.Template -Server $vc )){
        if($vm.Config.Template){
            $t = Get-View -Id (Get-View $vm.Runtime.Host).Parent
        }
        else{
            $t = Get-View $vm.ResourcePool -Property Name,Parent -Server $vc
            while($t.getType().Name -eq "ResourcePool"){
              $t = Get-View $t.Parent -Property Name,Parent -Server $vc
            }
        }
        if($t.GetType().Name -eq "ClusterComputeResource"){
            $cluster = $t.Name
        }
        else{
            $cluster = "Stand Alone Host"
        }
        while($t.getType().Name -ne "Datacenter"){
            $t = Get-View $t.Parent -Property Name,Parent -Server $vc
        }
        $datacenter = $t.Name
    
        #$vm.Config.Hardware.Device | where {$_ -is [VMware.Vim.VirtualDisk]}  
        $vm |Select @{N='vCenter';E={$vc}},@{N="Datacenter";E={$datacenter}},@{N="Cluster";E={$cluster}},
        @{N="VM";E={$vm.Name}},
        @{N='powerState';E={$vm.runtime.powerState}},
        @{N='IP';E={[string]::Join(',',($vm.Guest.Net | %{$_.IpAddress | where{$_.Split('.').Count -eq 4} | %{$_}}))}},
        @{N='NumCPU';E={$vm.config.Hardware.NumCpu}},
        @{N='Memory GB';E={$vm.Config.Hardware.MemoryMB| %{[math]::Round($_/1kb,2)}}},
        @{N="TotalHDD"; E={($vm.Config.Hardware.Device | where{$_ -is [VMware.Vim.VirtualDisk]}).Count}},
        #@{N="ProvisionSpaceGB";E={$vm.Summary.Storage.UnCommitted| %{[math]::Round($_/1GB,2)}}},
        #@{N="UsedSpaceGB";E={$vm.Summary.Storage.Committed| %{[math]::Round($_/1GB,2)}}},
        @{N='VMHost';E={$script:esx = Get-View -Id $vm.Runtime.Host -Server $vc ; $script:esx.name}},
        @{N='VMHost_Version';E={$script:esx = Get-View -Id $vm.Runtime.Host -Server $vc ; (Get-VMHost $Script:esx.name).ExtensionData.Config.Product.FullName }},
        @{N='VMHost_Make';E={$script:esx = Get-View -Id $vm.Runtime.Host -Server $vc ; Get-VMHost $Script:esx.name | select -ExpandProperty Manufacturer}},
        @{N='VMHost_Model';E={$script:esx = Get-View -Id $vm.Runtime.Host -Server $vc ; Get-VMHost $Script:esx.name | select -ExpandProperty Model}},
        @{N='VMHost_SerialNo';E={$script:esx = Get-View -Id $vm.Runtime.Host -Server $vc ; $script:esxcli = Get-EsxCli -VMHost $Script:esx.name; $script:esxcli.hardware.platform.get().SerialNumber}},
        @{N='GuestOS';E={$vm.Guest.GuestFullName}},
        @{N='ConfiguredOS';E={$vm.Config.GuestFullName}},
        @{N="VMware Tools version";E={$_.Config.Tools.ToolsVersion}},
        #@{N='DNSHostname';E={$_.guest.hostname}},
        @{N='VMConfigFile';E={$vm.config.files.VMpathname}},
        @{N='PGName';E={(Get-View -Id $_.Network -Property Name).Name -join '|'}}
        }

        $report_VMs1 |Export-Excel -Path $filename -WorksheetName "VM_Inventory" -AutoSize -FreezeTopRow -ClearSheet -TableName "VM_Inventory" #-TableStyle Light12

$report_Hosts2=foreach ($vmhost in Get-VMHost -State Connected,Maintenance -Server $vc){

        Get-VMHost $vmhost | Select Name,PowerState,State, IsStandalone,@{N='Product';E={$_.ExtensionData.Config.Product.FullName}},@{N=“Memory GB“;E={“” + [math]::round(($_| get-view).Hardware.MemorySize / 1GB, 0) }},@{N="CPU Sockets";E={($_| Get-View).Hardware.CpuInfo.NumCpuPackages}},
    @{N="PCores";E={($_| Get-View).Hardware.CpuInfo.NumCpuCores}},LicenseKey,#@{N="ManagementIP"; E={Get-VMHostNetworkAdapter -VMHost $_ -VMKernel | ?{$_.ManagementTrafficEnabled} | %{$_.Ip}}},LicenseKey, #@{N='LicenseName';E={$vmhostLM.AssignedLicense.Name | Select-Object -Unique}},@{N='ExpirationDate';E={$_.AssignedLicense.Properties.where{$_.Key -eq 'expirationDate'}.Value }},
@{N='vCenter';E={$_.Uid.Split('@')[1].Split(':')[0]}},
@{N="Cluster Name";E={($_ | Get-Cluster).Name}},@{N=”Datacenter”;E={Get-Datacenter -VMHost $_}},
Manufacturer, Model, ProcessorType,@{N='Serial';E={
$script:esxcli = Get-EsxCli -VMHost $_.Name

$script:esxcli.hardware.platform.get().SerialNumber }}

} 
$report_Hosts2 | Export-Excel -Path $filename -WorksheetName "VMHost_Inventory" -AutoSize -FreezeTopRow -ClearSheet -TableName "VMHost_Inventory" #-TableStyle Light13
$fromaddress = "XXX"
$toaddress = "XXX"
$Subject = "VM Inventory for vCenters: $VC."
$attachment = $filename
#$attachment1 = $filename1
$smtpserver = "XXXX"
$message = new-object System.Net.Mail.MailMessage
$message.From = $fromaddress
$message.To.Add($toaddress)
#$message.To.Add($toaddress1)
#$message.To.Add($toaddress2)
#$message.cc.Add($CCaddress)
#$message.cc.Add($CCaddress1)
#$message.cc.Add($CCaddress2)
#$message.IsBodyHtml = $True
$message.Subject = $Subject
$attach = new-object Net.Mail.Attachment($attachment)
$message.Attachments.Add($attach)
#$attach1 = new-object Net.Mail.Attachment($attachment1)
#$message.Attachments.Add($attach1)

$message.body = $body
$smtp = new-object Net.Mail.SmtpClient($smtpserver)

$MailText = @"
Hi Team,

PFA the VM inventory report for $VC vCenters.

$global:DefaultVIServers | select Name



===================================
Report generated on:- $CurrentDate.

===================================


Regards,
Naveen
 
"@
 
   $message.Body = $MailText
   $smtp.Send($message)



}

$jobs = foreach($vc in $vcNames){

    Start-Job -Name vcConnect -ScriptBlock $code -ArgumentList $vc | Select -ExpandProperty Id

}

Receive-Job -Wait -Id $jobs | %{

    Connect-VIServer -Server $_.Split(',')[0] -Session $_.Split(',')[1]

}

 

Reply
0 Kudos
LucD
Leadership
Leadership

You can use Import-Excel for all the generated XLSX files and then use Export-Excel each file to a separate worksheet in 1 XLSX file.

The lines for DnsHostName, ProvisionedSpaceGb and UsedSpaceGB are commented out in your code.

 There is no PortgroupName property, but there is a PgName property.

 


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

Reply
0 Kudos
swamynaveen
Enthusiast
Enthusiast

@LucD  Could you provide an example for import-excel and export-excel usage methods? As I never performed this. 
The lines which I have put in comments are not showing any data in the excel sheet due to which I marked them like that. When I look for $vm | select * with get-view method it doesn’t showing any values for most of the properties.

 

Regards,

Naveen

Reply
0 Kudos
kwhornlcs
Enthusiast
Enthusiast

It wasn't asked but what versions of vCenter, ESXi and PowerCLI are you using? (updated: see Powershell 5 was mentioned in earlier post)

There are a number of opportunities to optimize your property selection in addition to processing parallel, but where those properties are selected from could be limited by versions.

Reply
0 Kudos