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.

Revisions

  1. Sharkrit revised this gist Oct 18, 2025. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion Collect-ClusterDiagnostics.ps1
    Original file line number Diff line number Diff line change
    @@ -11,7 +11,7 @@

    param(
    [int]$HoursBack = 2,
    [string]$OutputRoot = "C:\ClusterDiag"
    [string]$OutputRoot = $PWD
    )

    Import-Module FailoverClusters -ErrorAction Stop
  2. Sharkrit renamed this gist Oct 18, 2025. 1 changed file with 0 additions and 0 deletions.
    File renamed without changes.
  3. Sharkrit created this gist Oct 18, 2025.
    134 changes: 134 additions & 0 deletions gistfile1.txt
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,134 @@
    <#
    .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 = "C:\ClusterDiag"
    )

    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"