Modules/Private/Export-S2DExcelReport.ps1
|
# Excel report exporter — uses ImportExcel module function Export-S2DExcelReport { param( [Parameter(Mandatory)] [S2DClusterData] $ClusterData, [Parameter(Mandatory)] [string] $OutputPath, [string] $Author = '', [string] $Company = '' ) if (-not (Get-Module -ListAvailable -Name ImportExcel -ErrorAction SilentlyContinue)) { throw "The 'ImportExcel' module is required for Excel reports. Install it with: Install-Module ImportExcel -Scope CurrentUser" } Import-Module ImportExcel -ErrorAction Stop $dir = Split-Path $OutputPath -Parent if (-not (Test-Path $dir)) { New-Item -ItemType Directory -Path $dir -Force | Out-Null } if (Test-Path $OutputPath) { Remove-Item $OutputPath -Force } $wf = $ClusterData.CapacityWaterfall $pool = $ClusterData.StoragePool $vols = @($ClusterData.Volumes) $disks = @($ClusterData.PhysicalDisks) $hc = @($ClusterData.HealthChecks) # ── Tab 1: Summary ──────────────────────────────────────────────────────── $summary = [PSCustomObject]@{ ClusterName = $ClusterData.ClusterName NodeCount = $ClusterData.NodeCount CollectedAt = $ClusterData.CollectedAt OverallHealth = $ClusterData.OverallHealth RawCapacityTiB = if ($wf) { $wf.RawCapacity.TiB } else { 'N/A' } RawCapacityTB = if ($wf) { $wf.RawCapacity.TB } else { 'N/A' } UsableCapacityTiB = if ($wf) { $wf.UsableCapacity.TiB } else { 'N/A' } UsableCapacityTB = if ($wf) { $wf.UsableCapacity.TB } else { 'N/A' } ReserveStatus = if ($wf) { $wf.ReserveStatus } else { 'N/A' } BlendedEfficiency = if ($wf) { "$($wf.BlendedEfficiencyPercent)%" } else { 'N/A' } PoolFriendlyName = if ($pool) { $pool.FriendlyName } else { 'N/A' } PoolHealthStatus = if ($pool) { $pool.HealthStatus } else { 'N/A' } PoolTotalTiB = if ($pool -and $pool.TotalSize) { $pool.TotalSize.TiB } else { 'N/A' } PoolAllocatedTiB = if ($pool -and $pool.AllocatedSize){ $pool.AllocatedSize.TiB } else { 'N/A' } PoolRemainingTiB = if ($pool -and $pool.RemainingSize){ $pool.RemainingSize.TiB } else { 'N/A' } OvercommitRatio = if ($pool) { $pool.OvercommitRatio } else { 'N/A' } Author = $Author Company = $Company } $summary | Export-Excel -Path $OutputPath -WorksheetName 'Summary' -AutoSize -FreezeTopRow -BoldTopRow # ── Tab 2: Capacity Waterfall ───────────────────────────────────────────── if ($wf) { $wfData = $wf.Stages | ForEach-Object { [PSCustomObject]@{ Stage = $_.Stage Name = $_.Name SizeTiB = if ($_.Size) { $_.Size.TiB } else { 0 } SizeTB = if ($_.Size) { $_.Size.TB } else { 0 } SizeBytes = if ($_.Size) { $_.Size.Bytes } else { 0 } DeltaTiB = if ($_.Delta) { $_.Delta.TiB } else { $null } Description = $_.Description Status = $_.Status } } $wfData | Export-Excel -Path $OutputPath -WorksheetName 'Capacity Waterfall' -AutoSize -FreezeTopRow -BoldTopRow -Append } # ── Tab 3: Physical Disks ───────────────────────────────────────────────── $diskData = $disks | ForEach-Object { [PSCustomObject]@{ NodeName = $_.NodeName FriendlyName = $_.FriendlyName SerialNumber = $_.SerialNumber MediaType = $_.MediaType BusType = $_.BusType Role = $_.Role SizeTiB = if ($_.Size) { $_.Size.TiB } else { 0 } SizeTB = if ($_.Size) { $_.Size.TB } else { 0 } Model = $_.Model FirmwareVersion = $_.FirmwareVersion HealthStatus = $_.HealthStatus OperationalStatus = $_.OperationalStatus WearPercentage = $_.WearPercentage Temperature = $_.Temperature PowerOnHours = $_.PowerOnHours ReadErrors = $_.ReadErrors WriteErrors = $_.WriteErrors } } $diskData | Export-Excel -Path $OutputPath -WorksheetName 'Physical Disks' -AutoSize -FreezeTopRow -BoldTopRow -Append # ── Tab 4: Storage Pool ─────────────────────────────────────────────────── if ($pool) { $poolData = [PSCustomObject]@{ FriendlyName = $pool.FriendlyName HealthStatus = $pool.HealthStatus OperationalStatus = $pool.OperationalStatus IsReadOnly = $pool.IsReadOnly TotalSizeTiB = if ($pool.TotalSize) { $pool.TotalSize.TiB } else { 0 } AllocatedSizeTiB = if ($pool.AllocatedSize) { $pool.AllocatedSize.TiB } else { 0 } RemainingSizeTiB = if ($pool.RemainingSize) { $pool.RemainingSize.TiB } else { 0 } ProvisionedSizeTiB = if ($pool.ProvisionedSize){ $pool.ProvisionedSize.TiB } else { 0 } OvercommitRatio = $pool.OvercommitRatio FaultDomainAwareness = $pool.FaultDomainAwareness } $poolData | Export-Excel -Path $OutputPath -WorksheetName 'Storage Pool' -AutoSize -FreezeTopRow -BoldTopRow -Append } # ── Tab 5: Volumes ──────────────────────────────────────────────────────── $volData = $vols | ForEach-Object { [PSCustomObject]@{ FriendlyName = $_.FriendlyName FileSystem = $_.FileSystem ResiliencySettingName = $_.ResiliencySettingName NumberOfDataCopies = $_.NumberOfDataCopies ProvisioningType = $_.ProvisioningType SizeTiB = if ($_.Size) { $_.Size.TiB } else { 0 } FootprintOnPoolTiB = if ($_.FootprintOnPool) { $_.FootprintOnPool.TiB } else { 0 } AllocatedSizeTiB = if ($_.AllocatedSize) { $_.AllocatedSize.TiB } else { 0 } EfficiencyPercent = $_.EfficiencyPercent HealthStatus = $_.HealthStatus OperationalStatus = $_.OperationalStatus IsDeduplicationEnabled = $_.IsDeduplicationEnabled IsInfrastructureVolume = $_.IsInfrastructureVolume } } $volData | Export-Excel -Path $OutputPath -WorksheetName 'Volumes' -AutoSize -FreezeTopRow -BoldTopRow -Append # ── Tab 6: Health Checks ────────────────────────────────────────────────── $hcData = $hc | ForEach-Object { [PSCustomObject]@{ CheckName = $_.CheckName Severity = $_.Severity Status = $_.Status Details = $_.Details Remediation = $_.Remediation } } $hcData | Export-Excel -Path $OutputPath -WorksheetName 'Health Checks' -AutoSize -FreezeTopRow -BoldTopRow -Append # ── Tab 7: Raw Data (JSON) ──────────────────────────────────────────────── $rawJson = $ClusterData | ConvertTo-Json -Depth 8 -Compress [PSCustomObject]@{ RawJson = $rawJson } | Export-Excel -Path $OutputPath -WorksheetName 'Raw Data' -AutoSize -Append Write-Verbose "Excel report written to $OutputPath" $OutputPath } |