Hey Checkyourlogs Fans,
Recently I have been working on a Storage Spaces Direct project with an amazing scripter and Dev/OPS admin named Sammy Shuck. He has taken the liberty to clean up the code in the Clear-SDSConfig.PS1. As many of you know I love to use this script to factory reset Storage Spaces Direct Nodes. The source script can be found at the Technet Scripting Gallery.
https://gallery.technet.microsoft.com/scriptcenter/Completely-Clearing-an-ab745947

The issue with this script is there isn’t much for logging included with it. So, what Sammy has done is added some additional warning contexts to the code and then generate an eventlog entry when the script is run.
You can check out his code and his Github Repositories here: https://raw.githubusercontent.com/ToxicSamN/powershell/master/hyperv/Nuke-ClusterAndS2D.ps1
Let’s have a look at the script in action shall we.
For my test I have recently updated to Windows Server 2019 LTSC on some of my lab nodes and they have an orphaned Storage Spaces Direct Configuration.
When I run Get-PhysicalDisk you can see the output with my node having a bunch of disks with Lost Communication.

I just want to Factory Reset Storage Spaces Direct and prepare these nodes to be rejoined to a newly created Cluster. When we do the factory reset I expect that the disks will come back to a state of CanPool =True
On with the show…. I have downloaded the code form Sammy’s Github repo and I just need to run this via an Administrative PowerShell Prompt.
Here is the output by simply running this on the node.


After running the script my disks were returned to Disk Management and it appears to have nicely cleaned them for me.

Let’s try Get-PhysicalDisk on more time.

As we can see the CanPool property is now True which means these are ready for me to commence the build of Storage Spaces Direct again.
Finally, let’s have a look at the EventID 1337 that was created as part of this script’s execution.

Here is the code from the script. I think this one just might make it into Master PowerShell Tricks V4.
################################## WARNING ################################
# #
# *** USE AT YOUR OWN RISK *** #
# PERMANANT DATA LOSS WILL OCCUR #
# #
# This script completely clears any existing Storage Spaces configuration #
# and all data on EVERY non-system drive PERMANENTLY! #
# #
# Notes: #
# #
# If certain drives cannot be cleared and the reason given is #
# 'Redundant Path' then MPIO may need to be installed and/or configured. #
# #
# Power cycling the JBOD enclosures can also remove additional #
# errors encountered during the run. #
# #
# Run cmdlet with Administrator rights. #
# #
################################## WARNING ################################
################################# Change Log ################################
# #
# 02/13/2014: Changed logic to remove SAS-connected boot/system disks #
# 02/13/2014: Changed output for clearing disks and tracking runtime #
# 04/07/2014: Corrected logic to deal boot and system drives #
# 04/07/2014: Added logic to deal with non-core cluster objects #
# 07/23/2015: Changes to better support Storage Spaces Direct #
# 04/23/2018: Added warnings and Event logging (github.com/ToxicSamN) #
# #
#############################################################################
[CmdletBinding(SupportsShouldProcess=$true, ConfirmImpact="High")]
param()
$warning_msg1 = "PERMANANT DATA LOSS WILL OCCUR!!!`nIf your intention is to Nuke the configuration of ONLY THIS NODE $($env:ComputerName) then you MUST ensure this node has been evicted from the cluster first.`nIf not then this could Nuke the configuration of EVERY node in the cluster.`nExercise caution!"
$confirm_prompt1 = "By Continuing you are acknowledging that you understand this process fully and understand this will completely wipe ALL data and there is NO recovery.`nWould you like to continue? (y/n): "
$confirm_prompt2 = "This is your last chance, there is no going back after this. Data will be lost.`nAre you absolutely sure you would like to continue? (y/n): "
Write-Warning $warning_msg1
$usr_input1 = Read-Host $confirm_prompt1
if ($usr_input1 -eq "y" -or $usr_input1 -eq "yes"){
$usr_input2 = Read-Host $confirm_prompt2
if ($usr_input2 -ne "y" -and $usr_input2 -ne "yes") { exit -1 }
else{
$nuke_info = "Username: $(whoami)`nComputerName: $($env:ComputerName)`nClusterStatus: `n`tMemberOf: $((Get-Cluster).Name)`nMessage: Node was wiped with script Nuke-ClusterAndS2D.ps1 by user $(whoami)`n
`nUser was warned: `n$($warning_msg1)
`nUser Acknowledged Data Destruction:`n$($confirm_prompt1) $($usr_input1)
`nUser Was warned a final time: `n$($confirm_prompt2) $($usr_input2)
"
Write-Warning "The following information is being logged: $($nuke_info)"
# attempt to register a new Event Log source. If already registered then throw an exception and continue. this suppresses the error
try{New-EventLog -LogName "System" -Source "Nuke-ClusterAndS2D" -MessageResourceFile "C:\utils\NukeClusterS2D.dll" -ErrorAction Stop | Out-Null}catch{<#Do Nothing#>}
Write-EventLog -LogName "System" -Source "Nuke-ClusterAndS2D" -EventID 1337 -EntryType Warning -Message $nuke_info
}
}else{ exit -1 }
if ($PSCmdlet.ShouldProcess("localhost","Clear Storage Spaces configuration and wipe disks"))
{
Write-Host ""
Write-Host Clearing existing Storage Spaces configuration and wiping disks...
Write-Host ""
$runStart = [DateTime]::Now
# Install necessary tools if needed
$toolsInstalled = $false
if (!(Get-WindowsFeature -Name "RSAT-Clustering-PowerShell").Installed)
{
Write-Host Installing required tools... -ForegroundColor Cyan -NoNewline
Install-WindowsFeature -Name "RSAT-Clustering-PowerShell"
$toolsInstalled = $true
Write-Host Done.
Write-Host ""
}
# Remove any cluster objects if present
Write-Host "Removing any cluster objects" -NoNewline -ForegroundColor Cyan
Write-Host "..." -NoNewline
foreach ($clusterGroup in (Get-ClusterGroup -ErrorAction SilentlyContinue -WarningAction SilentlyContinue))
{
if (!$clusterGroup.IsCoreGroup)
{
Remove-ClusterGroup -Name $clusterGroup.Name -Force:$true -RemoveResources:$true -ErrorAction SilentlyContinue
}
}
Remove-Cluster -Force -CleanupAD -ErrorAction SilentlyContinue -WarningAction SilentlyContinue
Write-Host "Done."
$disks = Get-PhysicalDisk | Where-Object {($_.BusType -EQ "SAS") -or ($_.BusType -EQ "SATA")} # -or ($_.BusType -EQ "RAID")}
Write-Host ""
Write-Host "Removing any stale PRs" -NoNewline -ForegroundColor Cyan
Write-Host "..." -NoNewline
foreach ($disk in $disks)
{
Clear-ClusterDiskReservation -Disk $disk.DeviceId -Force -ErrorAction SilentlyContinue -WarningAction SilentlyContinue
}
Write-Host "Done."
Write-Host ""
Write-Host "Updating the storage provider cache (x2)" -NoNewline -ForegroundColor Cyan
Write-Host "..." -NoNewline
Update-StorageProviderCache -DiscoveryLevel Full
Start-Sleep 1
Update-StorageProviderCache -DiscoveryLevel Full
Write-Host "Done."
# Remove virtual disks and storage pools
Write-Host ""
Write-Host "Removing Virtual Disks and Pools" -NoNewline -ForegroundColor Cyan
Write-Host "..." -NoNewline
$storagePools = Get-StoragePool | ? FriendlyName -NE "primordial"
$storagePools | Set-StoragePool -IsReadOnly:$false
Get-VirtualDisk | Set-VirtualDisk -IsManualAttach:$false
Get-VirtualDisk | Remove-VirtualDisk -Confirm:$false
$storagePools | Remove-StoragePool -Confirm:$false
Write-Host "Done."
Write-Host ""
Write-Host "Updating the storage provider cache (x2)" -NoNewline -ForegroundColor Cyan
Write-Host "..." -NoNewline
Update-StorageProviderCache -DiscoveryLevel Full
Start-Sleep 1
Update-StorageProviderCache -DiscoveryLevel Full
Write-Host "Done."
Write-Host ""
# Collect IDs of any system/boot disks
$disks = Get-Disk
$diskIdsToRemove = @()
foreach ($disk in $disks)
{
if ($disk.IsBoot -or $disk.IsSystem)
{
$diskIdsToRemove += $disk.UniqueId
}
}
# Get collection of physical disks
$allPhysicalDisks = Get-PhysicalDisk | Where-Object {($_.BusType -EQ "SAS") -or ($_.BusType -EQ "SATA")} # -or ($_.BusType -EQ "RAID")}
# Create a new collection of physical disks without any system/boot disks
$physicalDisks = @()
foreach ($physicalDisk in $allPhysicalDisks)
{
$addDisk = $true
foreach ($diskIdToRemove in $diskIdsToRemove)
{
if ($physicalDisk.UniqueId -eq $diskIdToRemove)
{
$addDisk = $false
}
}
if ($addDisk)
{
$physicalDisks += $physicalDisk
}
}
# Iterate through all remaining physcial disks and wipe
Write-Host "Cleaning disks" -ForegroundColor Cyan -NoNewline
Write-Host "..."
$totalDisks = $physicalDisks.Count
$counter = 1
foreach ($physicalDisk in $physicalDisks)
{
$disk = $physicalDisk | Get-Disk
# Make sure disk is Online and not ReadOnly otherwise, display reason
# and continue
$disk | Set-Disk –IsOffline:$false -ErrorAction SilentlyContinue
$disk | Set-Disk –IsReadOnly:$false -ErrorAction SilentlyContinue
# Re-instantiate disks to update changes
$disk = $physicalDisk | Get-Disk
if ($disk.IsOffline -or $disk.IsReadOnly)
{
Write-Host "Warning: " -NoNewline -ForegroundColor Yellow
Write-Host "Unable to process disk " -NoNewline
Write-Host $disk.Number -NoNewline
Write-Host ": Offline Reason: " -NoNewline
Write-Host ($disk.OfflineReason) -NoNewline -ForegroundColor Yellow
Write-Host ", HealthStatus: " -NoNewline
Write-Host $disk.HealthStatus -ForegroundColor Yellow
}
else
{
Write-Host "Cleaning disk " -NoNewline
Write-Host $disk.Number -NoNewline -ForegroundColor Cyan
Write-Host " (" -NoNewline
Write-Host $counter -NoNewline -ForegroundColor Cyan
Write-Host " of " -NoNewline
Write-Host $totalDisks -NoNewline -ForegroundColor Cyan
Write-Host ")..." -NoNewline
# Wipe disk and initialize
$disk | ? PartitionStyle -NE "RAW" | Clear-Disk -RemoveData -RemoveOEM -Confirm:$false
$disk | Initialize-Disk -PartitionStyle GPT
Write-Host Done.
}
$counter++
}
# Remove any installed roles/tools
if ($toolsInstalled)
{
Write-Host Uninstalling Failover Cluster tool... -NoNewline -ForegroundColor Cyan
Remove-WindowsFeature -Name "Failover-Clustering","RSAT-Clustering-PowerShell"
Write-Host Done.
}
Write-Host ""
Write-Host "Updating the storage provider cache (x2)" -NoNewline -ForegroundColor Cyan
Write-Host "..." -NoNewline
Update-StorageProviderCache -DiscoveryLevel Full
Start-Sleep 1
Update-StorageProviderCache -DiscoveryLevel Full
Write-Host "Done."
# Output physical disk counts
Write-Host ""
Write-Host Physical Disks:
Get-PhysicalDisk | Group-Object Manufacturer,Model,MediaType,Size | ft Count,Name -AutoSize
Write-Host Configuration and data cleared!
Write-Host ""
Write-Host "Run duration: " -NoNewline
Write-Host ([Math]::Round((([DateTime]::Now).Subtract($runStart)).TotalMinutes,2)) -ForegroundColor Yellow -NoNewline
Write-Host " minutes"
}
Thanks,
Dave

Hey Dave, I know this is an old one but there is a small change that makes it better:
$allPhysicalDisks = Get-PhysicalDisk | Where-Object {($_.BusType -EQ “SAS”) -or ($_.BusType -EQ “SATA”)} # -or ($_.BusType -EQ “RAID”)}
should really be:
$allPhysicalDisks = Get-PhysicalDisk | Where-Object {($_.BusType -EQ “SAS”) -or ($_.BusType -EQ “SATA”) -or ($_.BusType -EQ “NVME”)} # -or ($_.BusType -EQ “RAID”)}
For those who have NVME disks in the S2D configurations.