Skip to content

Instantly share code, notes, and snippets.

@Sharkrit
Last active October 18, 2025 11:36
Show Gist options
  • Save Sharkrit/7589be27268a739dd223d8d1ffdec54c to your computer and use it in GitHub Desktop.
Save Sharkrit/7589be27268a739dd223d8d1ffdec54c to your computer and use it in GitHub Desktop.
Collect-ClusterDiagnostics.ps1
<#
.SYNOPSIS
Collect complete diagnostics for a Windows Failover Cluster including:
Cluster config, Preferred Owners, RDMA, SMB, SR-IOV, Hyper-V, and Storage Spaces Direct (S2D)
.NOTES
Author: GPT-5
Requires: Administrator privilege, FailoverClusters, Hyper-V, Storage modules
Usage: .\Collect-ClusterDiagnostics.ps1 -HoursBack 3 -OutputRoot "D:\ClusterDiag"
#>
param(
[int]$HoursBack = 2,
[string]$OutputRoot = $PWD
)
Import-Module FailoverClusters -ErrorAction Stop
Import-Module Hyper-V -ErrorAction SilentlyContinue
Import-Module Storage -ErrorAction SilentlyContinue
$timeStamp = Get-Date -Format "yyyyMMdd_HHmmss"
$outputDir = Join-Path -Path $OutputRoot -ChildPath "ClusterDiag_$timeStamp"
New-Item -Path $outputDir -ItemType Directory -Force | Out-Null
Function Save-ToFile {
param($Name, $Object)
$path = Join-Path $outputDir $Name
if ($Object -is [string]) {
$Object | Out-File -FilePath $path -Encoding UTF8
} else {
$Object | Out-String -Width 4096 | Out-File -FilePath $path -Encoding UTF8
}
}
# --- Core Cluster Info ---
Write-Host "Collecting cluster configuration..."
$cluster = Get-Cluster
Save-ToFile "01_Cluster_Info.txt" $cluster
Save-ToFile "02_Cluster_Nodes.txt" (Get-ClusterNode)
Save-ToFile "03_Cluster_Networks.txt" (Get-ClusterNetwork)
Save-ToFile "04_Cluster_Groups.txt" (Get-ClusterGroup | Select Name, OwnerNode, State, @{N='PreferredOwners';E={($_ | Get-ClusterOwnerNode) -join ','}})
Save-ToFile "05_Cluster_Resources.txt" (Get-ClusterResource)
Save-ToFile "06_Cluster_Quorum.txt" (Get-ClusterQuorum)
# --- RDMA / SR-IOV / NIC info ---
$nodes = (Get-ClusterNode).Name
foreach ($n in $nodes) {
Write-Host "Collecting NIC/RDMA/SRIOV from $n..."
$nicInfo = Invoke-Command -ComputerName $n -ScriptBlock { Get-NetAdapter | Select Name, InterfaceDescription, Status, LinkSpeed, VmqEnabled, SriovSupport, RDMAEnabled }
$rdma = Invoke-Command -ComputerName $n -ScriptBlock { Get-NetAdapterRdma -ErrorAction SilentlyContinue }
$sriov = Invoke-Command -ComputerName $n -ScriptBlock { Get-NetAdapterSriov -ErrorAction SilentlyContinue }
Save-ToFile "Node_${n}_NICs.txt" $nicInfo
Save-ToFile "Node_${n}_RDMA.txt" $rdma
Save-ToFile "Node_${n}_SRIOV.txt" $sriov
}
# --- SMB & Storage Network ---
Save-ToFile "07_SMB_ServerConfig.txt" (Get-SmbServerConfiguration)
Save-ToFile "08_SMB_ServerNetworkInterface.txt" (Get-SmbServerNetworkInterface)
Save-ToFile "09_SMB_Multichannel.txt" (Get-SmbMultichannelConnection -ErrorAction SilentlyContinue)
# --- Hyper-V Section ---
Write-Host "Collecting Hyper-V info..."
foreach ($n in $nodes) {
try {
$vms = Invoke-Command -ComputerName $n -ScriptBlock { Get-VM | Select Name, State, CPUUsage, MemoryAssigned, Uptime, Version }
$vSwitch = Invoke-Command -ComputerName $n -ScriptBlock { Get-VMSwitch | Select Name, SwitchType, BandwidthReservationMode, IovEnabled, IovQueuePairsRequested, Notes }
$vNic = Invoke-Command -ComputerName $n -ScriptBlock { Get-VMNetworkAdapter -All | Select VMName, Name, MacAddress, SwitchName, IPAddresses, IsManagementOs }
$vhd = Invoke-Command -ComputerName $n -ScriptBlock { Get-VMHardDiskDrive | Select VMName, Path, ControllerType, ControllerNumber }
Save-ToFile "Node_${n}_HyperV_VMs.txt" $vms
Save-ToFile "Node_${n}_HyperV_vSwitch.txt" $vSwitch
Save-ToFile "Node_${n}_HyperV_vNIC.txt" $vNic
Save-ToFile "Node_${n}_HyperV_VHDs.txt" $vhd
} catch {
Save-ToFile "Node_${n}_HyperV_Error.txt" $_
}
}
# --- S2D / Storage Spaces Direct ---
Write-Host "Collecting S2D health and storage information..."
try {
$s2dHealth = Get-ClusterS2D
Save-ToFile "10_S2D_ClusterS2D.txt" $s2dHealth
Save-ToFile "11_S2D_HealthReport.txt" (Get-StorageSubSystem *cluster* | Get-StorageHealthReport)
Save-ToFile "12_S2D_StoragePool.txt" (Get-StoragePool)
Save-ToFile "13_S2D_VirtualDisks.txt" (Get-VirtualDisk)
Save-ToFile "14_S2D_PhysicalDisks.txt" (Get-PhysicalDisk)
Save-ToFile "15_S2D_CacheDevices.txt" (Get-PhysicalDisk | Where-Object { $_.Usage -eq 'Journal' })
Save-ToFile "16_S2D_Enclosures.txt" (Get-StorageEnclosure)
Save-ToFile "17_S2D_FaultDomains.txt" (Get-StorageFaultDomain)
Save-ToFile "18_S2D_Tiers.txt" (Get-StorageTier)
} catch {
Save-ToFile "S2D_Error.txt" $_
}
# --- Performance Counters (S2D + CSV) ---
Write-Host "Collecting performance counters (S2D + CSV)..."
try {
$csvPerf = Get-Counter '\Cluster CSVFS(* )\*' -ErrorAction SilentlyContinue
$s2dPerf = Get-Counter '\Cluster Storage Spaces Direct(*)\*' -ErrorAction SilentlyContinue
Save-ToFile "19_Perf_CSVFS.txt" $csvPerf
Save-ToFile "20_Perf_S2D.txt" $s2dPerf
} catch {
Save-ToFile "Perf_Error.txt" $_
}
# --- Event Logs ---
$startTime = (Get-Date).AddHours(-$HoursBack)
Write-Host "Collecting Event Logs from $startTime..."
foreach ($n in $nodes) {
try {
$evtFailover = Invoke-Command -ComputerName $n -ScriptBlock { param($t) Get-WinEvent -FilterHashtable @{LogName='Microsoft-Windows-FailoverClustering/Operational'; StartTime=$t} } -ArgumentList $startTime
$evtHyperV = Invoke-Command -ComputerName $n -ScriptBlock { param($t) Get-WinEvent -FilterHashtable @{LogName='Microsoft-Windows-Hyper-V-Worker/Admin'; StartTime=$t} } -ArgumentList $startTime
$evtS2D = Invoke-Command -ComputerName $n -ScriptBlock { param($t) Get-WinEvent -FilterHashtable @{LogName='Microsoft-Windows-StorageSpaces-Driver/Operational'; StartTime=$t} } -ArgumentList $startTime
Save-ToFile "Node_${n}_Evt_Failover.txt" $evtFailover
Save-ToFile "Node_${n}_Evt_HyperV.txt" $evtHyperV
Save-ToFile "Node_${n}_Evt_S2D.txt" $evtS2D
} catch {
Save-ToFile "Node_${n}_EventError.txt" $_
}
}
# --- Cluster Logs ---
Write-Host "Generating cluster logs..."
$clusterLogDir = Join-Path $outputDir "ClusterLogs"
New-Item $clusterLogDir -ItemType Directory -Force | Out-Null
Get-ClusterLog -Destination $clusterLogDir -UseLocalTime -Force | Out-Null
# --- Compress results ---
$zipPath = Join-Path $OutputRoot ("ClusterDiag_$timeStamp.zip")
Compress-Archive -Path "$outputDir\*" -DestinationPath $zipPath -Force
Write-Host "โœ… Diagnostics complete."
Write-Host "๐Ÿ“‚ Output folder: $outputDir"
Write-Host "๐Ÿ—œ๏ธ Zipped file: $zipPath"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment