# fixClusterDiskErrors.ps1
function selectClusterName{
param($domainName=$env:USERDNSDOMAIN)
write-host "Now scanning $domainName for all available cluster names. This may take a while...`r`n";
$clusterNames=(get-cluster -Domain $domainName).Name
$clusterList=@();
$index=0;
foreach ($clustername in $clusterNames){
$clusterList+=[PSCustomObject]@{Index=$index++;ClusterName=$clustername};
}

function makeSelection{
param($list)
$input=Read-Host "Please pick an index number from the above list";
#write-host "Index value $input received.";
$indexMax=$list.count-1
if ($input -ge 0 -and $input -le $indexMax){
#write-host "Index value $input is valid.";
return $input;
}else{
write-host "Index value $input is invalid.";
return $false;
}
}
$maxAttempts=3;
$attempts=1;
do {
cls |out-null;
write-host "Attempt number: $attempts of $maxAttempts`r`n";
$clusterList| Format-Table | Out-String | Write-Host;
$attempts++;
$selectedNumber=makeSelection -list $clusterList
if ($selectedNumber){
$selectedIndex=[convert]::ToInt16($selectedNumber)
<# This error would occur if: $selectedIndex=[int]$selectedNumber;
Cannot convert the "System.Object[]" value of type "System.Object[]" to type "System.Int32".
At line:6 char:9
+ $selectedIndex=[int]$selectedItem;
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidArgument: (:) [], RuntimeException
+ FullyQualifiedErrorId : ConvertToFinalInvalidCastException

Cannot convert the "System.Object[]" value of type "System.Object[]" to type "System.Int32".
At line:7 char:9
+ $clusterName=$clusterList[[int]$selectedIndex];
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidArgument: (:) [], RuntimeException
+ FullyQualifiedErrorId : ConvertToFinalInvalidCastException

#>
$clusterName=$clusterList[[int]$selectedIndex].ClusterName;
write-host "$clusterName is selected.";
return $clusterName;
}else{
if($attempts -gt $maxAttempts){break;}
sleep 2;
}
} while($selectedNumber -eq $false)
return $false;
}

# Take role offline > set disk online > run fixdisk > set role online
function fixClusterDiskErrors{
param([string]$clusterName=(get-cluster).name)
[int]$maxAttempts=3;

function getAllClusterDisks($clusterName){
$allClusterDisks=@();
$index=0;
$cluster=Get-ClusterResource -Cluster $clusterName
$nodes=$cluster.OwnerNode.Name|select-object -Unique
$viableNode=$nodes[0]
$allDisks=$cluster| ? { $_.ResourceType.Name -eq "Physical Disk" } |sort -Property Name

foreach ($disk in $allDisks) {
$diskName = $disk.Name;
$assignedTo=$disk.OwnerGroup.Name;
$ownerNode=$disk.OwnerNode.Name;
$status=$disk.ResourceSpecificStatus;
$diskObject = gwmi MSCluster_Resource -ComputerName $viableNode -Namespace root/mscluster | ? { $_.Name -eq $diskName };
$disk = gwmi -Namespace root/mscluster -ComputerName $viableNode -Query "ASSOCIATORS OF {$diskObject} WHERE ResultClass=MSCluster_Disk";
$partition = gwmi -Namespace root/mscluster -ComputerName $viableNode -Query "ASSOCIATORS OF {$disk} WHERE ResultClass=MSCluster_DiskPartition";
$driveLetter = ($partition.Path)[0];
$allClusterDisks+=[PSCustomObject]@{Index=$index++;DiskName=$diskName;DriveLetter=$driveLetter;AssignedTo=$assignedTo;OwnerNode=$ownerNode;Status=$status};
}
return $allClusterDisks
}

function fixClusterDisk{
param(
[string]$clusterName,
[string]$diskName
)
$clusterDiskObject=Get-ClusterResource -Cluster $clusterName|?{$_.Name -eq $diskName}
$resource = gwmi MSCluster_Resource -Namespace root/mscluster |? { $_.Name -eq $diskName }
$disk = gwmi -Namespace root/mscluster -Query "ASSOCIATORS OF {$resource} WHERE ResultClass=MSCluster_Disk"
$diskLetter = (gwmi -Namespace root/mscluster -Query "ASSOCIATORS OF {$disk} WHERE ResultClass=MSCluster_DiskPartition").Path[0]
$ownerNode=$clusterDiskObject.OwnerNode

$session=new-pssession $ownerNode
invoke-command -Session $session -ScriptBlock{
param($includeFailoverClustersModule,$diskLetter,$diskName)
[scriptblock]::Create($includeFailoverClustersModule).invoke();
$groupName=(Get-ClusterResource -Name $diskName).OwnerGroup
Stop-ClusterGroup $groupName|out-null;
# Error: if step 3 is executed before step 2
#start-clusterresource : An error occurred while attempting to bring the resource 'prdechome19-n' online.
# The remote server has been paused or is in the process of being started
#At line:1 char:1
#+ start-clusterresource -name
#+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# + CategoryInfo : ResourceBusy: (:) [Start-ClusterResource], ClusterCmdletException
# + FullyQualifiedErrorId : SharingPaused,Microsoft.FailoverClusters.PowerShell.StartClusterResourceCommand
try{
Resume-ClusterResource $diskName|out-null;
Start-ClusterResource $diskName|out-null;
Repair-Volume -DriveLetter $diskLetter -OfflineScanAndFix;
Start-ClusterGroup $groupName|out-null;
}catch{
write-host "Unable to fix volume name $diskName";
write-host $Error;
}
} -ArgumentList ${function:includeFailoverClustersModule},$diskLetter,$diskName
remove-pssession -Session $session
}

function makeSelection{
$input=Read-Host "Please pick an index number from the above list";
write-host "Index value $input received.";
if ($input -ne ""){
$selection=$clusterDisks[$input];
$status=$selection.Status
$diskName=$selection.DiskName
}else{
write-host "Null input is invalid.";
return $false;
}
if ($selection){
if ($status){
return $diskName;
}else{
write-host "$diskName currently does not have corruption errors.";
return $false;
}
}else{
write-host "Index value $input is invalid.";
return $false;
}
}

write-host "Scanning $clusterName for all available disks. This may take a while...`r`n";
$clusterDisks=getAllClusterDisks -clusterName $clustername;
$attempts=1;
do {
cls;
write-host "Attempt number: $attempts of $maxAttempts`r`n";
$attempts++;
$clusterDisks|ft -AutoSize;
$diskLabel=makeSelection
if ($diskLabel){
$confirmed=confirmation;
if($confirmed){
write-host "Now fixing $diskLabel as selected.";
fixClusterDisk -clusterName $clustername -diskName $diskLabel;
}
}
if($attempts -gt $maxAttempts){break;}
sleep 2;
} while($diskLabel -eq $false)

}

$clusterName=selectClusterName
if ($clusterName){
fixClusterDiskErrors -clusterName $clustername;
}else{
write-host "no valid cluster names have been selected.";
}
<# Troubleshooting Notes
PS C:\Windows\system32> Get-StorageSubSystem *Cluster*

FriendlyName HealthStatus OperationalStatus
------------ ------------ -----------------
Clustered Windows Storage on CLUSTER007 Healthy OK

Get-PhysicalDisk -FriendlyName "BALOO" | Disable-StorageMaintenanceMode

$diskName="BALOO";$diskLetter="S"
Suspend-ClusterResource -Name $diskName
chkdsk "$diskLetter`:" /spotfix #Deprecated on Windows 2016

# Error
PS C:\Windows\system32> chkdsk "$diskLetter`:" /spotfix
The type of the file system is NTFS.

Chkdsk cannot run because the volume is in use by another
process. Chkdsk may run if this volume is dismounted first.
ALL OPENED HANDLES TO THIS VOLUME WOULD THEN BE INVALID.
Would you like to force a dismount on this volume? (Y/N) n

Chkdsk cannot run because the volume is in use by another
process. Would you like to schedule this volume to be
checked the next time the system restarts? (Y/N) n

# PowerShell 4.0+ disk repair utility
Stop-ClusterResource -Name $diskName
Repair-Volume -DriveLetter $diskLetter -OfflineScanAndFix
Resume-ClusterResource -Name $diskName

Error: this occurs when the clustered disk is suspended prior to action above
Repair-Volume : The repair failed
Activity ID: {e8c94f40-1191-4128-a023-f328ee535cad}
At line:1 char:1
+ Repair-Volume -DriveLetter $diskLetter -OfflineScanAndFix
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (StorageWMI:ROOT/Microsoft/...age/MSFT_Volume) [Repair-Volume], CimException
+ FullyQualifiedErrorId : StorageWMI 43007,Repair-Volume

# To resume disk
Resume-ClusterResource -Name $diskName
#>

Sample Output

Index DiskName    DriveLetter AssignedTo    OwnerNode      Status
----- -------- ----------- ---------- --------- ------
0 MONKEY02 A MONKEY02 SHERVER007
1 MONKEY10 E MONKEY10-n SHERVER007
2 MONKEY11 F MONKEY11-n SHERVER007
3 MONKEY12 G MONKEY12-n SHERVER007
4 MONKEY13 I MONKEY13-n SHERVER007
5 MONKEY14 J MONKEY14-n SHERVER007
6 MONKEY15 N MONKEY15-n SHERVER007
7 MONKEY16 O MONKEY16-n SHERVER007
8 MONKEY17 Q MONKEY17-n SHERVER007
9 MONKEY18 R MONKEY18-n SHERVER007 Chkdsk scan needed on volume \\?\Volume{4f3f4072-3be0-421...
10 MONKEY19 S MONKEY19-n SHERVER007
11 Snapshots B Snapshots SHERVER007


Please pick an index number from the above list: