Wednesday, November 18, 2015

Clear selected disks configuration on selected multiple servers with PowerShell

Sometimes we rebuild and test environments again and again. 
It is often required to rebuild and clear disks for new attempt.
Following PowerShell script (Windows 8/2012+) will clear selected disks, remove all partition information including OEM, un-initialize, erase all data 
on selected serversIt completely clears any existing storage disk configuration and all data on selected drives PERMANENTLY! It will ask you to confirm if any system or bootable drive detected, it will clear even dynamic disks.

Download PowerShell script from
https://gallery.technet.microsoft.com/Clear-selected-disks-e0026a28 
or copy below:

<#

            Author  : Stanislav Galchonkov
            Website : http://wintech.sgal.info
            Linkedin: https://ua.linkedin.com/in/stanislav-galchonkov-aa560042/
###############################  WARNING  ################################
                                                                  
                  ***    USE AT YOUR OWN RISK    ***              
                   PERMANANT DATA LOSS WILL OCCUR                  
                                                                  
Following PowerShell script (Windows 8/2012+) will clear selected disks, remove all
partition information including OEM, un-initialize, erase all data on
selected servers only. It completely clears any existing storage disk configuration
and all data on SELECTED drives PERMANENTLY! It will ask you to confirm if any
system or bootable drive detected. It will try to put online any offline disks
and clear "read-only" flag on disk if detected.
                                                    
Notes:                                                          
                                                              
Set-ExecutionPolicy -ExecutionPolicy Unrestricted.            
Run cmdlet with Administrator rights.

Usage:
.\ClearDisksOnServers.ps1 -DisksToClear (1..14) -Servers server1,server2,server3,server4,server5,server6

Usage:
.\ClearDisksOnServers.ps1 -DisksToClear (1,2,3,7,8,10,15,19) -Servers server1,server2,server3,server4,server5,server6

If you have CSV file with server list:
Usage:
.\ClearDisksOnServers.ps1 -DisksToClear (1..14) -Servers (Import-Csv Servers.csv).servers

where CSV file Servers.csv content should be with Servers header:

Servers
server1
server2
server3
server4
server5
server6
            
Change log:
11/19/2015: Initial release
  
#>

[CmdletBinding()]
Param(
[Parameter(Mandatory=$True)]
[ValidateNotNull()]
[Array]$DiskNumbersToClear,
  
[Parameter(Mandatory=$True)]
[ValidateNotNull()]
[Array]$Servers
)

# $DisksToClear = (1..14)
# $servers="server1","server2","server3","server4","server5","server6"

$runStart = [DateTime]::Now

foreach ($server in $servers)
{
    # Use dispart utility if dynamic disks detected
    # Check if you run script for local or remote server

    # If server is local then run commands locally otherwise remotely
    if ($env:COMPUTERNAME -eq $server)
    {
    # Detecting dynamic disks
    # Apply legacy clean method via diskpart utility for discovered dynamic disks

    $listdisk = "list disk" | diskpart
    $Parsing = [String]::Join("`n", $listdisk) | Select-String -Pattern "Disk (\d+).{38}(.)" -AllMatches
    $DynamicDisksMapping=$Parsing.Matches | Select-Object @{Name="DiskIndex";Expression={$_.Groups[1].Value}}, @{Name="Dynamic";Expression={$_.Groups[2].Value -eq "*"}}
    $DynamicDisks=($DynamicDisksMapping | where Dynamic).DiskIndex

    if ($DynamicDisks -ne $null)
        {foreach ($DynamicDisk in $DynamicDisks)
                {if ($DisksToClear -contains $DynamicDisk) {
                    #check if Dynamic disk is bootable or system
                    $attr="select disk $DynamicDisk","attributes disk" | diskpart
                    $Bootable=$attr[11].Split(":")
                    $System=$attr[12].Split(":")
                    if ($Bootable[1] -eq " Yes" -or $System[1] -eq " Yes") {Write-Host "Warning: " $Server "Disk" $DynamicDisk is system or bootable -ForegroundColor Yellow}
                        else {
                        Write-Host Disk $DynamicDisk is dynamic disk -ForegroundColor Yellow
                        "select disk $DynamicDisk","clean","exit" | diskpart | Out-Null}}}}
    } else

    # Run diskpart commands remotely
    {
    Invoke-Command -ComputerName $server -ArgumentList $DisksToClear -ScriptBlock  {param($DisksToClear);
    # Detecting dynamic disks
    # Apply legacy clean method via diskpart utility for discovered dynamic disks

    $listdisk = "list disk" | diskpart;
    $Parsing = [String]::Join("`n", $listdisk) | Select-String -Pattern "Disk (\d+).{38}(.)" -AllMatches;
    $DynamicDisksMapping=$Parsing.Matches | Select-Object @{Name="DiskIndex";Expression={$_.Groups[1].Value}}, @{Name="Dynamic";Expression={$_.Groups[2].Value -eq "*"}};
    $DynamicDisks=($DynamicDisksMapping | where Dynamic).DiskIndex;

    if ($DynamicDisks -ne $null)
        {foreach ($DynamicDisk in $DynamicDisks)
                {if ($DisksToClear -contains $DynamicDisk)
                    {
                    #check if Dynamic disk is bootable or system
                    $attr="select disk $DynamicDisk","attributes disk" | diskpart;
                    $Bootable=$attr[11].Split(":");
                    $System=$attr[12].Split(":");
                    if ($Bootable[1] -eq " Yes" -or $System[1] -eq " Yes") {Write-Host "Warning: " $Server "Disk" $DynamicDisk is system or bootable -ForegroundColor Yellow;}
                        else {
                        Write-Host Disk $DynamicDisk is dynamic disk -ForegroundColor Yellow;
                        "select disk $DynamicDisk","clean","exit" | diskpart | Out-Null;}}}}}
    }

    $session=New-CimSession -ComputerName $server

    foreach ($DiskN in $DisksToClear)
    {
        $Disk=Get-Disk -Number $DiskN -cimsession $session
        $Confirm=$false
        # Make sure disk is Online and not ReadOnly otherwise, display reason and continue
        if ($Disk.IsOffline)
        {
            # Try to put it online
            Set-Disk -Number $DiskN -cimsession $session –IsOffline:$false -ErrorAction SilentlyContinue
            # Re-instantiate disk to update changes
            $Disk=Get-Disk -Number $DiskN -cimsession $session
        }
        if ($Disk.IsReadOnly)
        {
            # Try to clear read-only
            Set-Disk -Number $DiskN -cimsession $session –IsReadOnly:$false -ErrorAction SilentlyContinue
            # Re-instantiate disk to update changes
            $Disk=Get-Disk -Number $DiskN -cimsession $session
        }
        # Display reason if Offline or Read Only
        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
        {    
        # Warn and confirm to proceed if disk is bootable or system
        if ($Disk.IsBoot -or $Disk.IsSystem)
            {
            Write-Host "Warning: " -NoNewline -ForegroundColor Yellow
            Write-Host $Server "Disk" $DiskN is system or bootable -NoNewline
            $Confirm=$true
            }
        if ($Disk.PartitionStyle -ne 'raw')
            {
            Write-Host $Server Clearing $Disk.FriendlyName DiskNumber $DiskN -ForegroundColor Green
            Clear-Disk -Number $DiskN -RemoveData –RemoveOEM -Cimsession $Session -Confirm:$Confirm
            # Initialize-Disk -Number $DiskN -Cimsession $Session -PartitionStyle MBR
            }
        }
    }
    # Output physical disk counts
    Write-Host ""
    Write-Host $server Disks found:
    Get-Disk -cimsession $session | Group-Object Manufacturer,Model,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"