update
This commit is contained in:
+177
@@ -0,0 +1,177 @@
|
||||
<#
|
||||
.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
|
||||
Reference in New Issue
Block a user