<#
.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
