Inventory VMware virtual machines

For reporting purposes, I maintain an Excel sheet containing information on my server base. I update this sheet on a weekly basis. Part of the information displayed in this sheet comes from a PowerShell script I have created to extract the required data from our vCenters.

This script pulls the following information for every VM running the vCenter environment: VM Name, Hardware version, VMware tools status, guest OS, number of vCPU’s, amount of assigned RAM, the host the VM runs on, the cluster the VM is assigned to, the folder it lives in, the power status, number of NICs, the NIC types and the vCenter it runs on (in case you have more than 1). Maybe this script is sufficient for your needs. If not, it should be straight forward enough for you to adjust. A requirement for this script to work is the VMware PowerCLI PowerShell module, which can be installed with this PowerShell command:

1
Install-Module -Name VMware.PowerCLI -Scope CurrentUser


See https://blogs.vmware.com/PowerCLI for more information about VMware PowerCLI.

In order for this script to work for your environment, make sure to adjust variable $vCenterServers to match your vCenter server(s).
Also have a look at variable $VMList. The script below simply gets information about all your virtual machines. But off course you can limit the number of queried virtual machines by using a filter. I have left an example as comments.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
If (-not (Get-Module -Name 'VMware.PowerCLI')) {
    Import-Module VMware.PowerCLI
}

# Set-PowerCLIConfiguration -DefaultVIServerMode Multiple -Scope Session  # Needs to be set only once !!!
# Set-PowerCLIConfiguration -DefaultVIServerMode Multiple -Scope User # Needs to be set only once !!!
# Set-PowerCLIConfiguration -InvalidCertificateAction ignore -confirm:$false

$DateTime = $((Get-Date).ToString('yyyy-MM-dd_hh-mm-ss'))
$OutputPath = "$env:USERPROFILE\Documents\Reports"

If ( -Not (Test-Path -Path $OutputPath)) {
    New-Item -ItemType directory -Path $OutputPath
}

Set-Location $OutputPath
$Outputfile = "$DateTime-All-VMs-Output"

Function Connect-vCenter {
    Param (
        [Parameter(Mandatory = $True)]
        $vCenterServer,
        [System.Management.Automation.PSCredential]$Credential
    )

    If ($Credential) {
        # Add-PSSnapin VMware.VimAutomation.Core
        Connect-VIServer $vCenterServer -Credential $Credential
    } Else {
        # Add-PSSnapin VMware.VimAutomation.Core
        Connect-VIServer $vCenterServer
    }
}

# Prompt for Credentials and verify them using the DirectoryServices.AccountManagement assembly.
Write-Host "Please provide your credentials so the script can continue."
Add-Type -AssemblyName System.DirectoryServices.AccountManagement
# Extract the current user's domain and also pre-format the user name to be used in the credential prompt.
$UserDomain = "$env:USERDOMAIN"
#$UserName = "$UserDomain\$env:USERNAME"
$UserName = "$env:USERNAME"
# Define the starting number (always #1) and the initial credential prompt message to use.
$Attempt = 1
$CredentialPrompt = "Enter your domain account password (attempt #$Attempt out of $MaxAttempts):"
# Set ValidAccount to false so it can be used to exit the loop when a valid account is found (and the value is changed to $True).
$ValidAccount = $False

# Loop through prompting for and validating credentials, until the credentials are confirmed, or the maximum number of attempts is reached.
Do {
    # Blank any previous failure messages and then prompt for credentials with the custom message and the pre-populated domain\user name.
    $FailureMessage = $Null
    $Credentials = Get-Credential -UserName $UserDomain\$UserName -Message $CredentialPrompt
    # Verify the credentials prompt wasn't bypassed.
    If ($Credentials) {
        # If the user name was changed, then switch to using it for this and future credential prompt validations.
        If ($Credentials.UserName -ne $UserName) {
            $UserName = $Credentials.UserName
        }
        # Test the user name (even if it was changed in the credential prompt) and password.
        $ContextType = [System.DirectoryServices.AccountManagement.ContextType]::Domain
        Try {
            $PrincipalContext = New-Object System.DirectoryServices.AccountManagement.PrincipalContext $ContextType, $UserDomain
        } Catch {
            If ($_.Exception.InnerException -like "*The server could not be contacted*") {
                $FailureMessage = "Could not contact a server for the specified domain on attempt #$Attempt out of $MaxAttempts."
            } Else {
                $FailureMessage = "Unpredicted failure: `"$($_.Exception.Message)`" on attempt #$Attempt out of $MaxAttempts."
            }
        }
        # If there wasn't a failure talking to the domain test the validation of the credentials, and if it fails record a failure message.
        If (-not($FailureMessage)) {
            $ValidAccount = $PrincipalContext.ValidateCredentials($UserName, $Credentials.GetNetworkCredential().Password)
            If (-not($ValidAccount)) {
                $FailureMessage = "Bad user name or password used on credential prompt attempt #$Attempt out of $MaxAttempts."
            }
        }
        # Otherwise the credential prompt was (most likely accidentally) bypassed so record a failure message.
    } Else {
        $FailureMessage = "Credential prompt closed/skipped on attempt #$Attempt out of $MaxAttempts."
    }

    # If there was a failure message recorded above, display it, and update credential prompt message.
    If ($FailureMessage) {
        Write-Warning "$FailureMessage"
        $Attempt++
        If ($Attempt -lt $MaxAttempts) {
            $CredentialPrompt = "Authentication error. Please try again (attempt #$Attempt out of $MaxAttempts):"
        } ElseIf ($Attempt -eq $MaxAttempts) {
            $CredentialPrompt = "Authentication error. THIS IS YOUR LAST CHANCE (attempt #$Attempt out of $MaxAttempts):"
        }
    }
} Until (($ValidAccount) -or ($Attempt -gt $MaxAttempts))

# If the credentials weren't successfully verified, then exit the script, otherwise pass them through for further use.
Write-Host ""
If (-not($ValidAccount)) {
    Write-Host -ForegroundColor Red "You failed $MaxAttempts attempts at providing a valid user credentials. Exiting the script now... "
    EXIT
} Else {
    #Write-Host 'Your confirmed credentials have been saved to the $Credentials variable and is available after this script finishes.'
    $Global:Credentials = $Credentials
}

$vCenterServers = @('vcenter01.yourdomain.local','vcenter02.yourdomain.local','vcenter03.yourdomain.local') # Remove or add vCenter's as required
Connect-VCenter $vCenterServers $Credentials

$VMList = $null
$VMList = Get-VM # Get's all the VM's running in the specified vCenter(s)
#$VMList = Get-VM | Where-Object {$_.Name -like "*text*"} # User this line to get info on specified VM's
$VMList = $VMList | Sort-Object -Property Name # Sort the VM list on alphabetical order

$Counter = 1
$Report = @()

While ($Counter -lt $VMList.Count) {
    ForEach ($VM in $VMList) {
        Write-Host $Counter"/"($VMList).Count "[$VM]"

        $VMView = $VM | Get-View
        $VMNICs = (Get-NetworkAdapter -VM $VM)

        $VMInfo = { } | Select-Object Name, PowerState, HardwareVersion, vCenter, Host, Cluster, ToolsStatus, GuestOS, NumCPU, MemGB, NumNICs, NIC0Type, NIC0IP, NIC0Mac, NIC1Type, NIC1IP, NIC1Mac, NIC2Type, NIC2IP, NIC2Mac, NIC3Type, NIC3IP, NIC3Mac, Folder

        $VMInfo.Name = $VM.Name
        $VMInfo.HardwareVersion = $VM.HardwareVersion
        $VMInfo.ToolsStatus = $VMView.Guest.ToolsStatus
        $VMInfo.GuestOS = (Get-VMGuest -VM $VM).OSFullName
        $VMInfo.NumCPU = $VM.NumCpu
        $VMInfo.MemGB = $VM.MemoryGB
        $VMInfo.Host = $VM.VMHost.Name
        $VMInfo.Cluster = $VM.VMHost.Parent.name
        $VMInfo.Folder = $VM.Folder.Name
        $VMInfo.PowerState = $VM.PowerState
        $VMInfo.NumNICs = $VMNICs.Count
        $VMInfo.NIC0Type = $VMNICs.Type[0]
        $VMInfo.NIC0IP = $VM.Guest.IPAddress[0]
        $VMInfo.NIC0Mac = $VMNICs.MacAddress[0]
        $VMInfo.NIC1Type = $VMNICs.Type[1]
        $VMInfo.NIC1IP = $VM.Guest.IPAddress[1]
        $VMInfo.NIC1Mac = $VMNICs.MacAddress[1]
        $VMInfo.NIC2Type = $VMNICs.Type[2]
        $VMInfo.NIC2IP = $VM.Guest.IPAddress[2]
        $VMInfo.NIC2Mac = $VMNICs.MacAddress[2]
        $VMInfo.NIC3Type = $VMNICs.Type[3]
        $VMInfo.NIC3IP = $VM.Guest.IPAddress[3]
        $VMInfo.NIC3Mac = $VMNICs.MacAddress[3]
        $VMInfo.vCenter = $VM.Uid.Substring($VM.Uid.IndexOf('@') + 1).Split(":")[0]

        $Report += $VMInfo
        $Counter++
    }
}

$Report | Export-Csv "$Outputfile.csv" -NoTypeInformation -UseCulture

Disconnect-VIserver $vCenterServers -Confirm:$False


As always, please keep in mind this script is tailored to my environment, but can be used as a template for your environment. I do not pretend to be a PowerShell guru and as such my script may not be perfect. I am open to suggestions  🙂
If you found this script useful, I’d appreciate it if you leave a comment.

Leave a Reply

Your email address will not be published. Required fields are marked *