forked from sbourdeaud/nutanix
-
Notifications
You must be signed in to change notification settings - Fork 0
/
get-AhvVmReport.ps1
executable file
·390 lines (368 loc) · 18.6 KB
/
get-AhvVmReport.ps1
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
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
<#
.SYNOPSIS
This script retrieves the complete list of virtual machines from Prism Central.
.DESCRIPTION
This script retrieves the complete list of virtual machines from Prism Central, including each VM specs and exports the results to csv in the current directory.
.PARAMETER help
Displays a help message (seriously, what did you think this was?)
.PARAMETER history
Displays a release history for this script (provided the editors were smart enough to document this...)
.PARAMETER log
Specifies that you want the output messages to be written in a log file as well as on the screen.
.PARAMETER debugme
Turns off SilentlyContinue on unexpected error messages.
.PARAMETER prismcentral
Nutanix Prism Central fully qualified domain name or IP address.
.PARAMETER prismCreds
Specifies a custom credentials file name (will look for %USERPROFILE\Documents\WindowsPowerShell\CustomCredentials\$prismCreds.txt on Windows or in $home/$prismCreds.txt on Mac and Linux).
.PARAMETER ngt
Retrieves additional VM information, including Nutanix Guest Tools status and version. This requires that the credentials to access Prism Element are the same as the ones used for Prism Central.
.EXAMPLE
.\get-AhvVmReport.ps1 -cluster ntnxc1.local
Connect to a Nutanix Prism Central VM of your choice and retrieve the list of VMs.
.LINK
http://github.com/sbourdeaud/nutanix
.NOTES
Author: Stephane Bourdeaud ([email protected])
Revision: February 6th 2021
#>
#region parameters
Param
(
#[parameter(valuefrompipeline = $true, mandatory = $true)] [PSObject]$myParam1,
[parameter(mandatory = $false)] [switch]$help,
[parameter(mandatory = $false)] [switch]$history,
[parameter(mandatory = $false)] [switch]$log,
[parameter(mandatory = $false)] [switch]$debugme,
[parameter(mandatory = $true)] [string]$prismcentral,
[parameter(mandatory = $false)] $prismCreds,
[parameter(mandatory = $false)] [switch]$ngt
)
#endregion
#region prepwork
$HistoryText = @'
Maintenance Log
Date By Updates (newest updates at the top)
---------- ---- ---------------------------------------------------------------
06/21/2019 sb Initial release.
04/02/2020 sb Do over to include sbourdeaud module.
02/06/2021 sb Replaced username with get-credential
################################################################################
'@
$myvarScriptName = ".\get-AhvVmReport.ps1"
if ($help) {get-help $myvarScriptName; exit}
if ($History) {$HistoryText; exit}
#check PoSH version
if ($PSVersionTable.PSVersion.Major -lt 5) {throw "$(get-date) [ERROR] Please upgrade to Powershell v5 or above (https://www.microsoft.com/en-us/download/details.aspx?id=50395)"}
#region module sbourdeaud is used for facilitating Prism REST calls
$required_version = "3.0.8"
if (!(Get-Module -Name sbourdeaud))
{
Write-Host "$(get-date) [INFO] Importing module 'sbourdeaud'..." -ForegroundColor Green
try
{
Import-Module -Name sbourdeaud -MinimumVersion $required_version -ErrorAction Stop
Write-Host "$(get-date) [SUCCESS] Imported module 'sbourdeaud'!" -ForegroundColor Cyan
}#end try
catch #we couldn't import the module, so let's install it
{
Write-Host "$(get-date) [INFO] Installing module 'sbourdeaud' from the Powershell Gallery..." -ForegroundColor Green
try {Install-Module -Name sbourdeaud -Scope CurrentUser -Force -ErrorAction Stop}
catch {throw "$(get-date) [ERROR] Could not install module 'sbourdeaud': $($_.Exception.Message)"}
try
{
Import-Module -Name sbourdeaud -MinimumVersion $required_version -ErrorAction Stop
Write-Host "$(get-date) [SUCCESS] Imported module 'sbourdeaud'!" -ForegroundColor Cyan
}#end try
catch #we couldn't import the module
{
Write-Host "$(get-date) [ERROR] Unable to import the module sbourdeaud.psm1 : $($_.Exception.Message)" -ForegroundColor Red
Write-Host "$(get-date) [WARNING] Please download and install from https://www.powershellgallery.com/packages/sbourdeaud/1.1" -ForegroundColor Yellow
Exit
}#end catch
}#end catch
}#endif module sbourdeaud
$MyVarModuleVersion = Get-Module -Name sbourdeaud | Select-Object -Property Version
if (($MyVarModuleVersion.Version.Major -lt $($required_version.split('.')[0])) -or (($MyVarModuleVersion.Version.Major -eq $($required_version.split('.')[0])) -and ($MyVarModuleVersion.Version.Minor -eq $($required_version.split('.')[1])) -and ($MyVarModuleVersion.Version.Build -lt $($required_version.split('.')[2]))))
{
Write-Host "$(get-date) [INFO] Updating module 'sbourdeaud'..." -ForegroundColor Green
Remove-Module -Name sbourdeaud -ErrorAction SilentlyContinue
Uninstall-Module -Name sbourdeaud -ErrorAction SilentlyContinue
try
{
Update-Module -Name sbourdeaud -Scope CurrentUser -ErrorAction Stop
Import-Module -Name sbourdeaud -ErrorAction Stop
}
catch {throw "$(get-date) [ERROR] Could not update module 'sbourdeaud': $($_.Exception.Message)"}
}
#endregion
Set-PoSHSSLCerts
Set-PoshTls
#endregion
#region variables
$myvarElapsedTime = [System.Diagnostics.Stopwatch]::StartNew()
#prepare our overall results variable
[System.Collections.ArrayList]$myvarResults = New-Object System.Collections.ArrayList($null)
$length=100 #this specifies how many entities we want in the results of each API query
$api_server_port = "9440"
#endregion
#region parameters validation
if (!$prismCreds)
{#we are not using custom credentials, so let's ask for a username and password if they have not already been specified
$prismCredentials = Get-Credential -Message "Please enter Prism credentials"
}
else
{ #we are using custom credentials, so let's grab the username and password from that
try
{
$prismCredentials = Get-CustomCredentials -credname $prismCreds -ErrorAction Stop
$username = $prismCredentials.UserName
$PrismSecurePassword = $prismCredentials.Password
}
catch
{
Set-CustomCredentials -credname $prismCreds
$prismCredentials = Get-CustomCredentials -credname $prismCreds -ErrorAction Stop
$username = $prismCredentials.UserName
$PrismSecurePassword = $prismCredentials.Password
}
$prismCredentials = New-Object PSCredential $username, $PrismSecurePassword
}
#endregion
#region main processing
#region make v3 api call for clusters (if -ngt)
if ($ngt)
{
[System.Collections.ArrayList]$myvarClusterResults = New-Object System.Collections.ArrayList($null)
$api_server_endpoint = "/api/nutanix/v3/clusters/list"
$url = "https://{0}:{1}{2}" -f $prismcentral,$api_server_port, $api_server_endpoint
$method = "POST"
# this is used to capture the content of the payload
$content = @{
kind="cluster";
offset=0;
length=$length
}
$payload = (ConvertTo-Json $content -Depth 4)
Write-Host "$(Get-Date) [INFO] Retrieving clusters information from Prism Central $($prismcentral)" -ForegroundColor Green
Do
{
try
{
$resp = Invoke-PrismAPICall -method $method -url $url -payload $payload -credential $prismCredentials
$listLength = 0
if ($resp.metadata.offset)
{
$firstItem = $resp.metadata.offset
}
else
{
$firstItem = 0
}
if (($resp.metadata.length -le $length) -and ($resp.metadata.length -ne 1))
{
$listLength = $resp.metadata.length
}
else
{
$listLength = $resp.metadata.total_matches
}
Write-Host "$(Get-Date) [INFO] Processing results from $($firstItem) to $($firstItem + $listLength) out of $($resp.metadata.total_matches)" -ForegroundColor Green
if ($debugme) {Write-Host "$(Get-Date) [DEBUG] Response Metadata: $($resp.metadata | ConvertTo-Json)" -ForegroundColor White}
#grab the information we need in each entity
ForEach ($entity in $resp.entities)
{
$myvarClusterInfo = [ordered]@{
"name" = $entity.spec.name;
"uuid" = $entity.metadata.uuid;
"external_ip" = $entity.spec.resources.network.external_ip;
"AOS" = $entity.spec.resources.config.software_map.NOS.version
}
#store the results for this entity in our overall result variable
$myvarClusterResults.Add((New-Object PSObject -Property $myvarClusterInfo)) | Out-Null
}
#prepare the json payload for the next batch of entities/response
$content = @{
kind="cluster";
offset=($resp.metadata.length + $resp.metadata.offset);
length=$length
}
$payload = (ConvertTo-Json $content -Depth 4)
}
catch
{
$saved_error = $_.Exception.Message
# Write-Host "$(Get-Date) [INFO] Headers: $($headers | ConvertTo-Json)"
Write-Host "$(Get-Date) [INFO] Payload: $payload" -ForegroundColor Green
Throw "$(get-date) [ERROR] $saved_error"
}
finally
{
#add any last words here; this gets processed no matter what
}
}
While ($resp.metadata.length -eq $length)
if ($debugme)
{
Write-Host "$(Get-Date) [DEBUG] Showing results:" -ForegroundColor White
$myvarClusterResults
}
}
#endregion
#region make v3 api call for vms
# this is used to capture the content of the payload
$content = @{
kind="vm";
offset=0;
length=$length
}
$payload = (ConvertTo-Json $content -Depth 4)
Write-Host "$(Get-Date) [INFO] Retrieving VM information from Prism Central $($prismcentral)" -ForegroundColor Green
Do
{
try
{
$api_server_endpoint = "/api/nutanix/v3/vms/list"
$url = "https://{0}:{1}{2}" -f $prismcentral,$api_server_port, $api_server_endpoint
$method = "POST"
$resp = Invoke-PrismAPICall -method $method -url $url -payload $payload -credential $prismCredentials
$listLength = 0
if ($resp.metadata.offset)
{
$firstItem = $resp.metadata.offset
}
else
{
$firstItem = 0
}
if (($resp.metadata.length -le $length) -and ($resp.metadata.length -ne 1))
{
$listLength = $resp.metadata.length
}
else
{
$listLength = $resp.metadata.total_matches
}
Write-Host "$(Get-Date) [INFO] Processing results from $($firstItem) to $($firstItem + $listLength) out of $($resp.metadata.total_matches)" -ForegroundColor Green
if ($debugme) {Write-Host "$(Get-Date) [DEBUG] Response Metadata: $($resp.metadata | ConvertTo-Json)" -ForegroundColor White}
#grab the information we need in each entity
ForEach ($entity in $resp.entities)
{
if ($ngt)
{
$myvarClusterIp = ($myvarClusterResults | Where-Object {$_.uuid -eq $entity.spec.cluster_reference.uuid}).external_ip
if (!$myvarClusterIp) {throw "$(get-date) [ERROR] Could not find external ip address of cluster $($entity.spec.cluster_reference.name)"}
$api_server_endpoint = "/PrismGateway/services/rest/v1/vms/?filterCriteria=vm_uuid%3D%3D{0}" -f $entity.metadata.uuid
$url = "https://{0}:{1}{2}" -f $myvarClusterIp,$api_server_port, $api_server_endpoint
$method = "GET"
try
{
Write-Host "$(Get-Date) [INFO] Retrieving detailed VM information for $($entity.spec.name) from cluster $($entity.spec.cluster_reference.name)" -ForegroundColor Green
$myvarVmDetails = Invoke-PrismAPICall -method $method -url $url -credential $prismCredentials
Write-Host "$(Get-Date) [SUCCESS] Successfully retrieved detailed VM information for $($entity.spec.name) from cluster $($entity.spec.cluster_reference.name)" -ForegroundColor Cyan
}
catch
{
$saved_error = $_.Exception.Message
# Write-Host "$(Get-Date) [INFO] Headers: $($headers | ConvertTo-Json)"
#Write-Host "$(Get-Date) [INFO] Payload: $payload" -ForegroundColor Green
Throw "$(get-date) [ERROR] $saved_error"
}
$myvarVmInfo = [ordered]@{
"name" = $entity.spec.name;
#"os" = $myvarVmDetails.entities.guestOperatingSystem;
"ip_addresses" = $myvarVmDetails.entities.ipAddresses -join ',';
"virtual_disks" = $myvarVmDetails.entities.nutanixVirtualDisks -join ',';
"flash_mode" = $myvarVmDetails.entities.vmFeatures.FLASH_MODE;
"description" = $myvarVmDetails.entities.description;
"ngt_status" = $myvarVmDetails.entities.nutanixGuestTools.enabled;
"ngt_version" = $myvarVmDetails.entities.nutanixGuestTools.installedVersion;
"ngt_vss_snapshot" = $myvarVmDetails.entities.nutanixGuestTools.applications.vss_snapshot;
"ngt_vss_file_level_restore" = $myvarVmDetails.entities.nutanixGuestTools.applications.file_level_restore;
"ngt_iso_mounted" = $myvarVmDetails.entities.nutanixGuestTools.toolsMounted;
"ngt_communication_alive" = $myvarVmDetails.entities.nutanixGuestTools.communicationLinkActive;
"num_sockets" = $entity.spec.resources.num_sockets;
"memory_size_mib" = $entity.spec.resources.memory_size_mib;
"power_state" = $entity.spec.resources.power_state;
"cluster" = $entity.spec.cluster_reference.name;
"hypervisor" = $entity.status.resources.hypervisor_type;
"creation_time" = $entity.metadata.creation_time;
"owner" = $entity.metadata.owner_reference.name;
"protection_type" = $entity.status.resources.protection_type;
"vdisk_count" = ($entity.spec.resources.disk_list | where-object {$_.device_properties.device_type -eq "DISK"}).Count;
"vdisk_total_mib" = ($entity.spec.resources.disk_list | where-object {$_.device_properties.device_type -eq "DISK"} | Measure-Object disk_size_mib -Sum).Sum;
"vnic_count" = ($entity.spec.resources.nic_list).Count;
"vnic_vlans" = (($entity.spec.resources.nic_list | Select-Object -Property subnet_reference).subnet_reference.name) -join ',';
"vnic_macs" = (($entity.spec.resources.nic_list | Select-Object -Property mac_address).mac_address) -join ',';
"gpu" = $entity.status.resources.gpu_list | Select-Object -First 1;
"uuid" = $entity.metadata.uuid
}
}
else
{
$myvarVmInfo = [ordered]@{
"name" = $entity.spec.name;
"num_sockets" = $entity.spec.resources.num_sockets;
"memory_size_mib" = $entity.spec.resources.memory_size_mib;
"power_state" = $entity.spec.resources.power_state;
"cluster" = $entity.spec.cluster_reference.name;
"hypervisor" = $entity.status.resources.hypervisor_type;
"creation_time" = $entity.metadata.creation_time;
"owner" = $entity.metadata.owner_reference.name;
"protection_type" = $entity.status.resources.protection_type;
"vdisk_count" = ($entity.spec.resources.disk_list | where-object {$_.device_properties.device_type -eq "DISK"}).Count;
"vdisk_total_mib" = ($entity.spec.resources.disk_list | where-object {$_.device_properties.device_type -eq "DISK"} | Measure-Object disk_size_mib -Sum).Sum;
"vnic_count" = ($entity.spec.resources.nic_list).Count;
"vnic0_vlan" = $entity.spec.resources.nic_list[0].subnet_reference.name;
"vnic0_mac" = $entity.spec.resources.nic_list[0].mac_address;
"vnic1_vlan" = $entity.spec.resources.nic_list[1].subnet_reference.name;
"vnic1_mac" = $entity.spec.resources.nic_list[1].mac_address;
"gpu" = $entity.status.resources.gpu_list | Select-Object -First 1;
"uuid" = $entity.metadata.uuid
}
}
#store the results for this entity in our overall result variable
$myvarResults.Add((New-Object PSObject -Property $myvarVmInfo)) | Out-Null
}
#prepare the json payload for the next batch of entities/response
$content = @{
kind="vm";
offset=($resp.metadata.length + $resp.metadata.offset);
length=$length
}
$payload = (ConvertTo-Json $content -Depth 4)
}
catch
{
$saved_error = $_.Exception.Message
# Write-Host "$(Get-Date) [INFO] Headers: $($headers | ConvertTo-Json)"
Write-Host "$(Get-Date) [INFO] Payload: $payload" -ForegroundColor Green
Throw "$(get-date) [ERROR] $saved_error"
}
finally
{
#add any last words here; this gets processed no matter what
}
}
While ($resp.metadata.length -eq $length)
if ($debugme)
{
Write-Host "$(Get-Date) [DEBUG] Showing results:" -ForegroundColor White
$myvarResults
}
Write-Host "$(Get-Date) [INFO] Writing results to $(Get-Date -UFormat "%Y_%m_%d_%H_%M_")VmList.csv" -ForegroundColor Green
$myvarResults | export-csv -NoTypeInformation $($(Get-Date -UFormat "%Y_%m_%d_%H_%M_")+"VmList.csv")
#endregion
#endregion
#region Cleanup
#let's figure out how much time this all took
Write-Host "$(Get-Date) [SUM] total processing time: $($myvarElapsedTime.Elapsed.ToString())" -ForegroundColor Magenta
#cleanup after ourselves and delete all custom variables
Remove-Variable myvar* -ErrorAction SilentlyContinue
Remove-Variable ErrorActionPreference -ErrorAction SilentlyContinue
Remove-Variable help -ErrorAction SilentlyContinue
Remove-Variable history -ErrorAction SilentlyContinue
Remove-Variable log -ErrorAction SilentlyContinue
Remove-Variable cluster -ErrorAction SilentlyContinue
Remove-Variable debugme -ErrorAction SilentlyContinue
#endregion