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