Files
PowerShell/vCenter Tools/CollectVMInventory.ps1
T

327 lines
13 KiB
PowerShell

param (
[string]$vCenter = "",
[string]$cluster = "",
[string]$filename = "vmexport.xlsx",
[string]$Customer = "",
[switch]$Help = $false,
[switch]$SaveCred = $false
)
# Variables
$fileVersion = "1.3.1"
$fileDate = "2026-07-02"
$scriptName = Split-Path -Path $PSCommandPath -Leaf
# Display Script Banner
Write-Host "--------------------------------------------------" -ForegroundColor White
Write-Host "Collect VM Information for Migration" -ForegroundColor Green
Write-Host "Version $($FileVersion) ($($FileDate))" -ForegroundColor Green
Write-Host "Written by Charlie Silverman" -ForegroundColor Green
Write-Host "--------------------------------------------------" -ForegroundColor White
function CheckModuleInstalled($moduleName, $command) {
Write-Host "Checking that package $moduleName is installed..." -NoNewLine
if (-not(get-command $command -erroraction silentlycontinue)) {
Write-Host " Missing" -ForegroundColor Red
if (-not (Get-Module -ListAvailable -Name $moduleName)) {
$response = ""
while (($response -ne "yes") -and ($response -ne "no")) {
$response = (Read-Host "ImportExcel module not instaled. Install now? (yes/no)").ToLower()
}
if ($response -eq "no") {
Write-Host "Operation Cancelled." -ForegroundColor Yellow
exit
}
Write-Host "Installing PowerShell module $moduleName..." -NoNewLine
Install-Module -Name $moduleName -Scope CurrentUser -Force
Write-Host " Success" -ForegroundColor Green
}
Write-Host "Loading PowerShell module $moduleName..." -NoNewLine
Import-Module $moduleName
Write-Host " Success" -ForegroundColor Green
} else {
Write-Host " Found" -ForegroundColor Green
}
}
# Check for ImportExcel and install if missing
CheckModuleInstalled -moduleName "VCF.PowerCLI" -command "connect-viserver"
CheckModuleInstalled -moduleName "ImportExcel" -command "import-excel"
$moduleName = "ImportExcel"
Import-Module ImportExcel
if ($Help) {
Write-Host
Write-Host "Purpose: Generate a migration spreadsheet from vCenter VMs."
Write-Host "Command Syntax:"
Write-Host " $scriptName (-vcenter [vcenter host name]) (-cluster ""[cluster]"") (-filename ""[filename]"") (-customer ""[customer]""" -foregroundcolor green
Write-Host "Command Line Options:"
Write-Host
Write-Host -NoNewLine " -vCenter " -foregroundcolor Green
Write-Host "- The FQDN or IP address of a vCenter server."
Write-Host -NoNewLine " -cluster " -foregroundcolor Green
Write-Host "- The name of the cluster as shown in vCenter."
Write-Host -NoNewLine " -customer " -foregroundcolor Green
Write-Host "- The name of the customer to display on the sheet title."
Write-Host -NoNewLine " -filename " -foregroundcolor Green
Write-Host "- The CSV filename to save the result."
Write-Host
Write-Host "If any of the information is not provided, you will be prompted. If a credential has been previously stored, it will"
Write-Host "be used again unless the -savecred option is specified."
Write-Host
Write-Host "If a filename is not specified, the file vmexport.csv will be created in the current directory."
Write-Host
exit
}
$timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
$quiet = Set-PowerCLIConfiguration -InvalidCertificateAction Ignore -Confirm:$false
if ($vCenter -eq "") {
$vCenter = Read-Host "Enter name of vCenter Server"
}
if ($SaveCred) {
$credential = Get-Credential -Message "Enter credentials for vCenter server $vCenter"
$credential | Export-Clixml -path $env:USERPROFILE\kistools-$vcenter.cred
Write-Host "Saving credentials."
}
if (Test-Path -path $env:USERPROFILE\kistools-$vcenter.cred -PathType Leaf) {
$credentials = Import-Clixml -path $env:USERPROFILE\kistools-$vcenter.cred
Write-Host "Using stored credentials"
} else {
$credentials = Get-Credential -Message "Enter credentials for vCenter server $vCenter"
if (!$credentials) {
Write-Host "ERROR: No credentials were entered" -foregroundcolor yellow
}
}
if (Test-Path -path $filename -PathType Leaf) {
Write-Host "File $filename already exists. Overwrite?" -ForegroundColor Yellow -NoNewLine
Write-Host " (Y/N) " -NoNewLine
if ((Read-Host) -eq "Y") {
Remove-Item -Path $filename
} else {
Write-Host "Operation Aborted."
exit
}
}
try {
Write-Host -NoNewLine "Connecting to vCenter $vcenter..."
$vcconn = Connect-VIServer $vcenter -Credential $credentials -ErrorAction SilentlyContinue
Write-Host " Success" -ForegroundColor green
} catch {
Write-Host " Failure" -ForegroundColor Yellow -BackgroundColor Red
exit
}
if ($cluster -eq "") {
$Clusterlist = Get-Cluster | Sort-Object
While (($cluster -eq "") -or (-not $cluster)) {
Write-Host "`nCluster Selection:`n"
for ($x = 0; $x -lt $Clusterlist.length; $x++) {
Write-Host "$(([string]($x+1)).PadLeft(3,' '))" -NoNewLine -ForegroundColor Green
Write-Host ": " -NoNewLine -ForegroundColor Gray
Write-Host "$($Clusterlist[$x].name)" -ForegroundColor Yellow
}
$Clusternum = Read-Host "`nSelect Cluster Number"
if ($Clusternum -as [int]) {
$Clusternum = [int]$Clusternum - 1
$cluster = $Clusterlist[$Clusternum]
}
}
}
try {
$mycluster = get-cluster $cluster -ErrorAction Stop
} catch {
Write-Host
Write-Host "Cluster $cluster cannot be found in vCenter $vCenter" -ForegroundColor Red -BackgroundColor Yellow -NoNewLine
Write-Host
Write-Host "Operation aborted."
exit
}
Write-Host "Gathering information about VMs in cluster $cluster..." -NoNewLine
$vmlist = $mycluster[0] | get-vm
$vmcount = $vmlist.length
$vmresponse = @()
for ($x = 0; $x -lt $vmlist.length; $x++) {
$percent = [Math]::Round(($x / $vmlist.length) * 100,0)
Write-Progress -ID 1 -Activity "Collecting VM Information" -Status "Processing VM $($x+1) of $($vmlist.length)" -PercentComplete $percent
$thisvm = $vmlist[$x];
$vmname = $thisvm.Name;
$vmcpu = $thisvm.NumCPU
$vmram = $thisvm.MemoryGB
$vmstorageprov = [Math]::Round($thisvm.ProvisionedSpaceGB,2)
$vmstorageused = [Math]::Round($thisvm.UsedSpaceGB,2)
if ((($thisvm | Get-HardDisk) | Where-Object StorageFormat -ne "Thin").length -gt 0) {
$vmdiskformat = "Thick"
} else {
$vmdiskformat = "Thin"
}
if ($thisvm | Get-Snapshot) {
$vmhassnapshots = "X"
} else {
$vmhassnapshots = ""
}
$vmnics = $thisvm | get-NetworkAdapter
$vmisomounted = (($thisvm | get-cddrive).IsoPath -ne $null) ? "X" : ""
if ($vmnics.length -gt 0) {
$vmnic1pg = $vmnics[0].NetworkName;
} else {
$vmnic1pg = "---";
}
if ($vmnics.length -gt 1) {
$vmnic2pg = $vmnics[1].NetworkName;
} else {
$vmnic2pg = "---";
}
if ($vmnics.length -gt 2) {
$vmnic3pg = $vmnics[2].NetworkName;
} else {
$vmnic3pg = "---";
}
$thisvmobj = [PSCustomObject]@{
"Priority" = ""
"Name" = $vmname
"CPU" = $vmcpu
"RAM" = $vmram
"Has ISO Mounted" = $vmisomounted
"Has Snapshots" = $vmhassnapshots
"Provisioned Storage" = $vmstorageprov
"Used Space Original" = $vmstorageused
"Used Space Now" = ""
"Calcualted Space" = ""
"Disk Format" = $vmdiskformat
"NIC1 Port Group" = $vmnic1pg
"NIC2 Port Group" = $vmnic2pg
"NIC3 Port Group" = $vmnic3pg
"VM Migrated" = $null
"DRS Group Updated" = $null
"Storage Migrated" = $null
"In Prog" = ""
"Migrating" = ""
"Complete" = ""
}
$vmresponse += $thisvmobj
}
Write-Progress -ID 1 -Completed
Write-Host " Success" -ForegroundColor Green
Write-Host "Writing information about $($vmresponse.length) VMs to file $filename..." -NoNewLine
try {
$sheetTitle = "VM Export for Cluster $cluster - $timestamp"
if ($customer -ne "") {
$sheetTitle = "$customer - $sheetTitle"
}
$exceltable = $vmresponse | Export-Excel -path $filename -WorksheetName "vCenter VM Data" -PassThru -Autosize -FreezeTopRow `
-TableName "VMData" -TableStyle Medium15 -Title $sheetTitle -TitleBold -TitleSize 22 `
-TableTotalSettings @{ CPU = "Sum"; RAM = "Sum"; "Provisioned Storage" = "Sum"; "Used Space Original" = "Sum" }
} catch {
Write-Host
Write-Host "ERROR! Unable to create file $filename" -ForegroundColor Yellow
Write-Host "Error: $($_.Exception.Message)" -ForegroundColor Red -BackgroundColor Yellow
}
try {
$sheet = $exceltable.Workbook.Worksheets["vCenter VM Data"]
$sheet.InsertColumn(1,1)
$sheet.Column(8) | Set-ExcelRange -NFormat '#,##0 " GB"'
$sheet.Column(9) | Set-ExcelRange -NFormat '#,##0.00 " GB"'
$sheet.Column(10) | Set-ExcelRange -NFormat '#,##0.00 " GB"'
$sheet.Column(11) | Set-ExcelRange -NFormat '#,##0.00 " GB"'
Set-Format -Address $sheet.Cells["2:2"] -WrapText
$sheet.Column(1).Width = 2
$sheet.Column(2).Width = 8
$sheet.Cells["I:T"].Style.HorizontalAlignment = "Center"
$sheet.Cells["D2:T2"].Style.HorizontalAlignment = "Center"
$sheet.Column(6).Width = 10
$sheet.Column(7).Width = 10
$sheet.Column(8).Width = 10
$sheet.Column(9).Width = 13
$sheet.Column(10).Width = 13
$sheet.Column(11).Width = 13
$sheet.Column(12).Width = 12
$sheet.Column(18).Width = 12
$sheet.Column(19).Width = 12
$sheet.Column(20).Width = 12
$sheet.Column(21).Width = 12
$sheet.Column(22).Width = 12
for($counter = 3; $counter -lt 3 + $vmcount; ++$counter) {
Write-Progress -ID 1 -Activity "Collecting VM Information" -Status "Processing VM $($counter-2) of $($vmcount)" -PercentComplete $percent
$CalculatedSpaceFormula = '=if(AND($J' + $counter + '>0,$J' + $counter + '<>""), $I' + $counter + ',"")'
Set-ExcelRange -Address $sheet.Cells["K" + $counter] -Formula $CalculatedSpaceFormula
$InProgFormula = '=IF(AND(COUNTIF(P' + $counter + ':R' + $counter + ',"X")<3,COUNTIF(P' + $counter + ':R' + $counter + ',"")<3),"X","")'
Set-ExcelRange -Address $sheet.Cells["S" + $counter] -Formula $InProgFormula
$MigratingFormula = '=IF(OR(P' + $counter + '="O",Q' + $counter + '="O",R' + $counter + '="O"),"X","")'
Set-ExcelRange -Address $sheet.Cells["T" + $counter] -Formula $MigratingFormula
$CompleteFormula = '=IF(OR(AND(P' + $counter + '="X",Q' + $counter + '="X",R' + $counter + '="X"),L' + $counter + '="O"),"X","")'
Set-ExcelRange -Address $sheet.Cells["U" + $counter] -Formula $CompleteFormula
}
Write-Progress -ID 1 -Completed
$datarange = "B3:U" + (2+$vmcount)
Add-ConditionalFormatting -Worksheet $sheet -Address $datarange -RuleType Expression -ConditionValue '=INDIRECT("VMData[@Disk Format]")="Thick"' -BackgroundColor Red -ForegroundColor White
Add-ConditionalFormatting -Worksheet $sheet -Address $datarange -RuleType Expression -ConditionValue '=INDIRECT("VMData[@Disk Format]")="O"' -BackgroundColor Orange
Add-ConditionalFormatting -Worksheet $sheet -Address $datarange -RuleType Expression -ConditionValue '=INDIRECT("VMData[@In Prog]")="X"' -BackgroundColor Yellow
Add-ConditionalFormatting -Worksheet $sheet -Address $datarange -RuleType Expression -ConditionValue '=INDIRECT("VMData[@Complete]")="X"' -BackgroundColor Green
$sheet.Cells["V4"].Value = "Total VMs:"
$sheet.Cells["V5"].Value = "Thick Provisioned:"
$sheet.Cells["V6"].Value = "Converting to Thin:"
$sheet.Cells["V7"].Value = "Partially Migrated:"
$sheet.Cells["V8"].Value = "In Progress:"
$sheet.Cells["V9"].Value = "Completed:"
$sheet.Cells["V10"].Value = "Remaining:"
$sheet.Cells["V12"].Value = "Original Space Usage:"
$sheet.Cells["V13"].Value = "New Space Usage:"
$sheet.Cells["V14"].Value = "Space Saved:"
$sheet.Cells["V15"].Value = "Savings:"
$sheet.Cells["V4:V15"].Style.HorizontalAlignment = "Right"
$sheet.Column(22).Width = 22
$sheet.Column(23).Width = 18
Set-ExcelRange -Address $sheet.Cells["W4"] -Formula '=COUNTA(VMData[Name])'
Set-ExcelRange -Address $sheet.Cells["W5"] -Formula '=COUNTIF(VMData[Disk Format], "<>Thin")'
Set-ExcelRange -Address $sheet.Cells["W6"] -Formula '=COUNTIF(VMData[Disk Format], "O")'
Set-ExcelRange -Address $sheet.Cells["W7"] -Formula '=COUNTIF(VMData[Migrating],"X")'
Set-ExcelRange -Address $sheet.Cells["W8"] -Formula '=COUNTIF(VMData[In Prog], "X")'
Set-ExcelRange -Address $sheet.Cells["W9"] -Formula '=COUNTIF(VMData[Complete], "X")'
Set-ExcelRange -Address $sheet.Cells["W10"] -Formula '=W4-W9'
$formula = '=SUM(H3:H' + (2+$vmcount) +')'
Set-ExcelRange -Address $sheet.Cells["W12"] -Formula $formula
Set-ExcelRange -Address $sheet.Cells["W12"] -NFormat '#,##0.00 " GB"'
Set-ExcelRange -Address $sheet.Cells["W13"] -Formula '=SUM(VMData[Used Space Now])'
Set-ExcelRange -Address $sheet.Cells["W13"] -NFormat '#,##0.00 " GB"'
Set-ExcelRange -Address $sheet.Cells["W14"] -Formula '=W12-W13'
Set-ExcelRange -Address $sheet.Cells["W14"] -NFormat '#,##0.00 " GB"'
Set-ExcelRange -Address $sheet.Cells["W15"] -Formula '=IF(W12>0,W14/W12,"")'
Set-ExcelRange -Address $sheet.Cells["W15"] -NFormat '#.00%'
$sheet.Column(11).Hidden = $true
} catch {
Write-Host
Write-Host "ERROR! Unable to create file $filename" -ForegroundColor Yellow
Write-Host "Error: $($_.Exception.Message)" -ForegroundColor Red -BackgroundColor Yellow
try {
Close-ExcelPackage $exceltable
if (Test-Path -Path $filename) {
Remove-Item $filename
}
exit
} catch {
if (Test-Path -Path $filename) {
Remove-Item $filename
}
}
}
$exceltable.Workbook.WorkSheets[1].Tables["VMData"].ShowFilter = $false
Close-ExcelPackage $exceltable
Write-Host " Success" -ForegroundColor Green