# VMware vCollector # Copyright VMware Inc. 2020 # # Author: Josh Miller # # Technical Contact: Josh Miller # Email: joshmiller@vmware.com # # Description: The script uses VMware's PowerCLI to connect to a vSphere # vCenter or standalone ESXi host and collects data required for assessing # license compliance. Data collected includes license keys, licenses # assignments, physical host details, and a list of virtual machines. # # DISCLAIMER: VMware offers this script as-is and makes no representations or # warranties of any kind whether express, implied, statutory, or other. # This includes, without limitation, warranties of fitness for a particular # purpose, title, non-infringement, course of dealing or performance, usage # of trade, absence of latent or other defects, accuracy, or the presence # or absence of errors, whether known or discoverable. In no event will # VMware be liable to You for any direct, special, indirect, incidental, # consequential, punitive, exemplary, or other losses, costs, expenses, or # damages arising out of Your use of this script. # Import the PowerCLI module or snapin if it is available # exit the script if it is not if (!(Get-Module -Name VMware.VimAutomation.Core) -and (Get-Module -ListAvailable -Name VMware.VimAutomation.Core)) { Write-Output "Loading the VMware Core Module..." Import-Module -Name VMware.VimAutomation.Core -ErrorAction SilentlyContinue if (!(Get-Module -Name VMware.VimAutomation.Core)) { # Error out if loading fails Write-Error "`nERROR: Cannot load the VMware Module. Please check that PowerCLI is installed." Exit } Write-Host "Module loaded." } elseif (!(Get-PSSnapin -Name VMware.VimAutomation.Core -ErrorAction SilentlyContinue) -and !(Get-Module -Name VMware.VimAutomation.Core)) { Write-Output "Loading the VMware Core Snapin..." Add-PSSnapin -PassThru VMware.VimAutomation.Core -ErrorAction SilentlyContinue if (!(Get-PSSnapin -Name VMware.VimAutomation.Core -ErrorAction SilentlyContinue)) { # Error out if loading fails Write-Error "`nERROR: Cannot load the VMware Snapin or Module. Please check that PowerCLI is installed." Exit } Write-Host "Snapin loaded." } # Disconnect any current connections $ErrorActionPreference = "SilentlyContinue" Disconnect-VIServer -Server $global:DefaultVIServers -Force -Confirm:$false $ErrorActionPreference = "Continue" # Get the location of the current script $scriptPath = split-path -parent $MyInvocation.MyCommand.Definition # Set the output path $outputPath = $scriptPath + "\VMware Data\vCollector\" if(!(Test-Path $outputPath)) { New-Item -ItemType Directory -Path $outputPath | Out-Null } # Get the current date $Date = Get-Date -Format "yyyy-MM-dd" # Get PowerCLI version $Version = Get-Module -Name VMware.VimAutomation.Core | Select Name,Version # Start the connection log Add-Content -Path "$outputPath\ConnectionLog $Date.txt" -Value "PowerCLI Version: $($Version.Version)" # Check and update the PowerCLI configurations Set-PowerCLIConfiguration -InvalidCertificateAction Warn -Scope Session -Confirm:$false | Out-Null $Config = Get-PowerCLIConfiguration -Scope Session Set-PowerCLIConfiguration -DefaultVIServerMode Multiple -Scope Session -Confirm:$false | Out-Null $Config = Get-PowerCLIConfiguration -Scope Session # Add config values to the connection log Add-Content -Path "$outputPath\ConnectionLog $Date.txt" -Value "InvalidCertificateAction: $($Config.InvalidCertificateAction)" Add-Content -Path "$outputPath\ConnectionLog $Date.txt" -Value "DefaultVIServerMode: $($Config.DefaultVIServerMode)" # Get server name or IP address from user Write-Host "`nPlease provide the name or IP address of the vCenter server or ESXi host" Write-Host " * ESXi hosts managed by a vCenter do not need to be scanned individually" Write-Host " * For vCenters in Enhanced Linked Mode, only one vCenter needs to be scanned" $ServerName = $Host.UI.ReadLine() # Connect to the provided host or vCenter server $Output = Connect-VIServer -Server $ServerName -Verbose *>&1 Add-Content -Path "$outputPath\ConnectionLog $Date.txt" -Value $Output # Check for errors if($Output -like "*incorrect user name or password*") { Write-Error "`nError: Cannot complete login due to an incorrect user name or password." if($global:DefaultVIServers.Count -eq 0) { $Cred = Get-Credential $Output = Connect-VIServer -Server $ServerName -Credential $Cred -Verbose *>&1 Add-Content -Path "$outputPath\ConnectionLog $Date.txt" -Value $Output } } if($global:DefaultVIServers.Count -eq 0) { Write-Error "`nERROR: Unable to connect to provided vCenter or host. Review connection log for details." Exit } # Get license data $ServiceInstance = Get-View ServiceInstance $LicenseManager = Get-View $ServiceInstance.Content.LicenseManager $Licenses = @() $LicenseManager.Licenses | % { $Licenses += New-Object PSObject -Property @{ Product = $_.Name LicenseKey = $_.LicenseKey Total = $_.Total Used = $_.Used Metric = $_.CostUnit ExpirationDate = ($_.Properties | Where {$_.Key -eq "ExpirationDate"} | Select -ExpandProperty Value) Info = ($_.Labels | Select -ExpandProperty Value) } } # Connect to the provided host or vCenter server in linked mode $Output = Connect-VIServer -Server $ServerName -AllLinked -Verbose *>&1 Add-Content -Path "$outputPath\ConnectionLog $Date.txt" -Value $Output # Get host data $Hosts = Get-VMHost | Select Name,Version,LicenseKey,APIVersion,ConnectionState,PowerState,@{N="Cluster";E={$_.Parent.Name}}, @{N="vCenterIP";E={$_.ExtensionData.Summary.ManagementServerIp}},@{N="NumCPU";E={$_.ExtensionData.Summary.Hardware.NumCpuPkgs}}, @{N="NumCores";E={$_.ExtensionData.Hardware.CpuInfo.NumCpuCores}},@{N="NumThreads";E={$_.ExtensionData.Hardware.CpuInfo.NumCpuThreads}}, @{N="VMCount";E={($_ | Get-VM).Count}} # Add license product name to hosts $Hosts | % { $Key = $_.PSObject.Properties["LicenseKey"].Value $Entry = $Licenses | Where LicenseKey -eq $Key | Select -First 1 $vCenter = "" $FQDN = "" if(($_.vCenterIP -ne $null) -and ($_.vCenterIP -ne "")) { $FQDN = [System.Net.Dns]::GetHostEntry($_.vCenterIP).HostName $vCenter = $FQDN.Split(".")[0] } if($_.Cluster -eq "host") { $_.Cluster = "" } $_ | Add-Member -Name "Product" -Value $Entry.PSObject.Properties["Product"].Value -MemberType NoteProperty $_ | Add-Member -Name "Metric" -Value $Entry.PSObject.Properties["Metric"].Value -MemberType NoteProperty $_ | Add-Member -Name "vCenter" -Value $vCenter -MemberType NoteProperty } # Get VM data $VMs = Get-VM | Select Name,@{N="DNS Name";E={$_.ExtensionData.Guest.Hostname}},PowerState,@{N="Status";E={$_.ExtensionData.OverallStatus}},VMHost, @{N="Guest OS";E={$_.ExtensionData.Guest.GuestFullName}},NumCpu # Export VM data to CSV $VMs | Export-Csv -Path "$outputPath\$ServerName VMs $Date.csv" -NoTypeInformation # Export host data to CSV $Hosts | Select Name,Product,Version,LicenseKey,ApiVersion,ConnectionState,PowerState,Cluster,vCenter,Metric,NumCpu,NumCores,NumThreads,VMCount | Export-Csv -Path "$outputPath\$ServerName Hosts $Date.csv" -NoTypeInformation # Export license data $Licenses | Select Product,LicenseKey,Total,Used,Metric,ExpirationDate,Info | Export-Csv -Path "$outputPath\$ServerName Licenses $Date.csv" -NoTypeInformation Write-Host "`nScan successful for:" foreach($vc in $global:DefaultVIServers) { Write-Host $vc.Name } # Disconnect server Disconnect-VIServer -Server $ServerName -Confirm:$false # Prompt to exit Write-Host "Press Enter to exit" $Host.UI.ReadLine() # SIG # Begin signature block # MIIFlwYJKoZIhvcNAQcCoIIFiDCCBYQCAQExCzAJBgUrDgMCGgUAMGkGCisGAQQB # gjcCAQSgWzBZMDQGCisGAQQBgjcCAR4wJgIDAQAABBAfzDtgWUsITrck0sYpfvNR # AgEAAgEAAgEAAgEAAgEAMCEwCQYFKw4DAhoFAAQUz6igBdlvTDMfEEHifjX+VXeT # taOgggMkMIIDIDCCAgigAwIBAgIQanZ3anIAlqpKvpMg7dfAHzANBgkqhkiG9w0B # AQsFADAoMSYwJAYDVQQDDB1WTXdhcmUgQ29tcGxpYW5jZSBDZXJ0aWZpY2F0ZTAe # Fw0yMDAzMDkyMzMyNTBaFw0yMTAzMDkyMzUyNTBaMCgxJjAkBgNVBAMMHVZNd2Fy # ZSBDb21wbGlhbmNlIENlcnRpZmljYXRlMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A # MIIBCgKCAQEAwzZo2fniNLO+F5Q0zE/FESXAYT8ckes4WnXXEfq3jU8AZAQGQTuX # IBroT4DDUd5ujiA5mef03LDsM26ikXIVB/M2nGELsh7J/dgk2Z+HHXsUCpU1PnE0 # Gkv8JvTbLkHsa3+7HFsuMUqkjXvTESECNLIf187ga8zAZlJGmBi1j1PaCP0hgthF # TkTU1spon0W61r0jktSgpLSIfcDILa84MNgy7mgYXL+2DnlnQd5izesy9yn6h0D/ # nJXxYbDwoUeOy6dZ08OnzNZm8yR1xIX0nQfLpjxtxvLM+2A/h15W+r9ScX8AbAX5 # 5YYkN1GDTN6GPMLHPOLozr9WPV5xcL0u1QIDAQABo0YwRDAOBgNVHQ8BAf8EBAMC # B4AwEwYDVR0lBAwwCgYIKwYBBQUHAwMwHQYDVR0OBBYEFJtqQrnhtZowBYxN5+Fv # CwY+ptHuMA0GCSqGSIb3DQEBCwUAA4IBAQAr4NBjG1zP7UnFA1iHwQBCaQfDSsx3 # RN7FBGPGaD8Po35aV9xgcBxWG3v7LjXJ+N5PHW9gsQ0+6xXq4TgViWpEymoeRGNL # AigR/rJeRsupr4ICHxXgJvpvwf3C6baUemXNVZ/Jw/oP8kOkU9Cp4Z8A2nV55Y0+ # aN/OiWPx6WrW5dY4f5ZFU8PcJ7K8y5RCgb6Q3rVldwRgWLqZEyN4yYvvyFcnI1bN # 8/GcGyD7dDXloyqQMfbWRcWT6IU8BEctLuOmxO7BYnTgTYHuEeOyl5oGm/DRfvAB # GwVMpjSfgsjhOFPdlyX01jc+g930Y8UKJQsAfxmeHhAOU/GTNK5ueQkbMYIB3TCC # AdkCAQEwPDAoMSYwJAYDVQQDDB1WTXdhcmUgQ29tcGxpYW5jZSBDZXJ0aWZpY2F0 # ZQIQanZ3anIAlqpKvpMg7dfAHzAJBgUrDgMCGgUAoHgwGAYKKwYBBAGCNwIBDDEK # MAigAoAAoQKAADAZBgkqhkiG9w0BCQMxDAYKKwYBBAGCNwIBBDAcBgorBgEEAYI3 # AgELMQ4wDAYKKwYBBAGCNwIBFTAjBgkqhkiG9w0BCQQxFgQUhI6Z5p39ntlMPlwf # DDatcuOltLswDQYJKoZIhvcNAQEBBQAEggEAD1shMF8VzIzR9NQukAmzZ/dT+USc # 3t6hglB+7dEgGn2UeparcNUF+h0LcNvMq3v534Z/kL8JoI88jhL/9ZksC+pvQFrA # /pTUBQ/cZHM4LkNzuppwf+nkZHLNBAXZy63YIgRUCa4/nz8mP/pPJH1MWfpEUE+A # yH+/K5T110cNb6xob7y7emN01OyRA4acvXrc+QuJXF62agbVWMIhs3y3JcHQofOd # 2lMb15mA7YOiPaiWt24jcgEKv3PSp4wBiyfaw8QadAKVD3vyb4UV8rINhTirvRi0 # bXxUPPzn7FRk2xMxDbRRjRwcl3NlaBw4JJAtxSlrzM+MpYRifSxkk2SqBw== # SIG # End signature block