178 lines
6.7 KiB
PowerShell
178 lines
6.7 KiB
PowerShell
<#
|
|
.SYNOPSIS
|
|
Daily VM metadata report for PowerBI trending and hardware capacity planning.
|
|
|
|
.DESCRIPTION
|
|
Collects VM metadata from vCenter including compute, storage, OS, and VMware Tools
|
|
information using only data available within vCenter (no direct guest connections).
|
|
Exports a timestamped CSV each run -- append daily runs to build a historical dataset
|
|
suitable for PowerBI trend analysis and physical hardware purchasing decisions.
|
|
|
|
.PARAMETER vCenterServers
|
|
One or more vCenter server hostnames. Defaults to itdvmvc1.nd.gov and itdvmvc2.nd.gov.
|
|
|
|
.PARAMETER DatacenterFilter
|
|
Wildcard filter applied to datacenter names. Defaults to 'Primary*'.
|
|
|
|
.PARAMETER OutputPath
|
|
Directory where CSV and log files are written.
|
|
Defaults to C:\ITDSCRIPT\Reports\VMMetadata.
|
|
|
|
.PARAMETER CredentialPath
|
|
Path to a saved PSCredential XML file for unattended/scheduled runs.
|
|
Create one interactively with:
|
|
Get-Credential | Export-Clixml -Path C:\ITDSCRIPT\Creds\vCenter.xml
|
|
When omitted the script prompts for credentials.
|
|
|
|
.EXAMPLE
|
|
# Interactive run
|
|
.\VMware-VMDailyMetadataReport.ps1
|
|
|
|
.EXAMPLE
|
|
# Scheduled / unattended run
|
|
.\VMware-VMDailyMetadataReport.ps1 -CredentialPath 'C:\ITDSCRIPT\Creds\vCenter.xml'
|
|
|
|
.NOTES
|
|
Scheduled Task suggestion (run as service account that owns the credential XML):
|
|
Program : powershell.exe
|
|
Args : -NonInteractive -ExecutionPolicy Bypass -File "C:\ITDSCRIPT\VMware-VMDailyMetadataReport.ps1" -CredentialPath "C:\ITDSCRIPT\Creds\vCenter.xml"
|
|
Trigger : Daily at 06:00
|
|
|
|
Guest OS disk capacity / used columns are populated only for powered-on VMs with
|
|
VMware Tools running; they will be empty for powered-off VMs and SRM placeholders.
|
|
#>
|
|
[CmdletBinding()]
|
|
param(
|
|
[string] $OutputPath = 'C:\temp\VM_Trends\'
|
|
)
|
|
|
|
#region --- Setup ---------------------------------------------------------------
|
|
|
|
$RunDate = Get-Date
|
|
$DateStamp = $RunDate.ToString('yyyyMMdd')
|
|
$Timestamp = $RunDate.ToString('yyyy-MM-dd HH:mm:ss')
|
|
|
|
if (-not (Test-Path -Path $OutputPath)) {
|
|
New-Item -ItemType Directory -Path $OutputPath | Out-Null
|
|
}
|
|
|
|
Start-Transcript -Path (Join-Path $OutputPath "VMMetadataReport_$DateStamp.log") -Append
|
|
|
|
#endregion
|
|
|
|
#region --- Build VMHost -> Cluster/Datacenter Lookups (avoids per-VM API calls) -
|
|
|
|
Write-Verbose 'Building host-to-cluster and host-to-datacenter maps...'
|
|
$HostClusterMap = @{}
|
|
$HostDatacenterMap = @{}
|
|
|
|
Get-Datacenter | ForEach-Object {
|
|
$DatacenterName = $_.Name
|
|
Get-VMHost -Location $_ | ForEach-Object {
|
|
$HostDatacenterMap[$_.Name] = $DatacenterName
|
|
}
|
|
}
|
|
|
|
Get-Cluster | ForEach-Object {
|
|
$ClusterName = $_.Name
|
|
Get-VMHost -Location $_ | ForEach-Object {
|
|
$HostClusterMap[$_.Name] = $ClusterName
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region --- Collect VM Data -----------------------------------------------------
|
|
|
|
Write-Verbose "Gathering VMs"
|
|
|
|
# Include ALL VMs (SRM placeholders flagged via column, not excluded).
|
|
# vCLS agent VMs are excluded -- they are vSphere internal and not customer workloads.
|
|
$AllVMs = Get-VM | Where-Object { $_.Name -notlike 'vCLS*' }
|
|
|
|
Write-Verbose "Processing $($AllVMs.Count) VMs..."
|
|
|
|
$Results = foreach ($VM in $AllVMs) {
|
|
|
|
$Ext = $VM.ExtensionData # single API object -- reuse for all fields
|
|
|
|
#--- SRM placeholder detection
|
|
$IsSRMPlaceholder = $Ext.Summary.Config.ManagedBy.Type -eq 'placeholderVm'
|
|
|
|
#--- Cluster / Datacenter (null-safe: standalone hosts have no cluster entry)
|
|
$ClusterName = $HostClusterMap[$VM.VMHost.Name]
|
|
$DatacenterName = $HostDatacenterMap[$VM.VMHost.Name]
|
|
|
|
#--- Storage platform parsed from datastore name convention: VMCLUSTER_LUN_PLATFORM_Desc
|
|
# Segment 2 = storage platform identifier (e.g. FS92, A9K).
|
|
# Cluster grouping uses the compute Cluster column -- no need to re-derive it here.
|
|
$StoragePlatforms = foreach ($DSName in $Ext.Config.DatastoreUrl.Name) {
|
|
$Segments = $DSName -split '_'
|
|
if ($Segments.Count -ge 3) { $Segments[2] }
|
|
}
|
|
$StoragePlatform = ($StoragePlatforms | Sort-Object -Unique) -join '; '
|
|
|
|
#--- VMware Tools guest disk info
|
|
# Populated only when Tools is running; null otherwise.
|
|
$GuestDiskCapacityGB = $null
|
|
$GuestDiskUsedGB = $null
|
|
if ($Ext.Guest.Disk) {
|
|
$TotalCapBytes = ($Ext.Guest.Disk | Measure-Object -Property Capacity -Sum).Sum
|
|
$TotalFreeBytes = ($Ext.Guest.Disk | Measure-Object -Property FreeSpace -Sum).Sum
|
|
$GuestDiskCapacityGB = [Math]::Round($TotalCapBytes / 1GB, 2)
|
|
$GuestDiskUsedGB = [Math]::Round(($TotalCapBytes - $TotalFreeBytes) / 1GB, 2)
|
|
}
|
|
|
|
[PSCustomObject]@{
|
|
# --- Identity & grouping
|
|
ReportDate = $Timestamp # for PowerBI time-series/trend axis
|
|
VMName = $VM.Name
|
|
Datacenter = $DatacenterName
|
|
Cluster = $ClusterName
|
|
PowerState = $VM.PowerState
|
|
IsSRMPlaceholder = $IsSRMPlaceholder
|
|
StoragePlatform = $StoragePlatform
|
|
GuestOS = $Ext.Guest.GuestFullName
|
|
|
|
# --- Compute
|
|
vCPUs = $VM.NumCpu
|
|
MemoryGB = $VM.MemoryGB
|
|
|
|
# --- Datastore-level storage
|
|
# ProvisionedSpaceGB : maximum the VM could consume (thin disks counted at max size)
|
|
# UsedSpaceGB : bytes actually committed on datastores right now
|
|
ProvisionedSpaceGB = [Math]::Round($VM.ProvisionedSpaceGB, 2)
|
|
UsedSpaceGB = [Math]::Round($VM.UsedSpaceGB, 2)
|
|
|
|
# --- Guest OS-level storage (from VMware Tools; null when Tools not running)
|
|
# GuestDiskCapacityGB : sum of all volume capacities seen inside the guest
|
|
# GuestDiskUsedGB : sum of space consumed across those volumes
|
|
GuestDiskCapacityGB = $GuestDiskCapacityGB
|
|
GuestDiskUsedGB = $GuestDiskUsedGB
|
|
|
|
# --- VMware Tools
|
|
# ToolsRunningStatus : guestToolsRunning | guestToolsNotRunning | guestToolsExecutingScripts
|
|
# ToolsVersionStatus : guestToolsCurrent | guestToolsNeedUpgrade | guestToolsUnmanaged | guestToolsTooNew
|
|
# ToolsVersion : numeric build version string reported by vCenter
|
|
ToolsRunningStatus = $Ext.Guest.ToolsRunningStatus
|
|
ToolsVersionStatus = $Ext.Guest.ToolsVersionStatus
|
|
ToolsVersion = $Ext.Guest.ToolsVersion
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region --- Export --------------------------------------------------------------
|
|
|
|
$OutputFile = Join-Path $OutputPath "VMMetadata_$DateStamp.csv"
|
|
$Results | Export-Csv -Path $OutputFile -NoTypeInformation
|
|
|
|
Write-Verbose "Exported $($Results.Count) VM records to: $OutputFile"
|
|
|
|
#endregion
|
|
|
|
#region --- Cleanup -------------------------------------------------------------
|
|
Stop-Transcript
|
|
|
|
#endregion
|