Code:
<# Microsoft_Clustered_Disks_Creation_V0.10.ps1

Purpose:
This snippet streamlines the process of creating physical disk volumes on a Windows host and automatically add them into an existing Microsoft cluster

What this script does:
- Automatically formats a list of pre-defined disks with user-specified cluster sizes
- Assigns available drive letters as paths to newly created volumes
- Systematically adds new disks into a Microsoft Cluster as Physical Disks with correct labels

Limitations:
- Currently, this program cannot reliably sets unitialized disks as online or offline. Thus, prior to starting this script,
the admin must set disks as online MANUALLY; otherwise, such disks will be skipped
- This script doesn't fix weird problems at the LUN protocol level, as fixing that requires a re-creation of the LUN volumes.

Additional troubleshooting documentation:

using diskpart (manual):
SELECT DISK 2
ONLINE DISK
CLEAN
CREATE PARTITION PRIMARY
FORMAT FS=NTFS UNIT=16K QUICK
ASSIGN LETTER=B #Change B to any other available access-path letter
ACTIVE
EXIT

Add to Cluster (manual):
$numero=7
$etiqueta="LUN007"
Get-Disk -Number $numero | Add-ClusterDisk | %{$_.Name=$etiqueta }

Report a list of physical disks available on the host:
PS C:\Windows\system32> Get-WmiObject -Class win32_volume | select name,blocksize
name blocksize
---- ---------
C:\ 4096
E:\ 16384
F:\ 16384
G:\ 16384
I:\ 16384
J:\ 16384
N:\ 16384
\\?\Volume{3b1730ae-2559-40e3-a547-ae6a91d2d540}\ 4096

View all available disks:
Get-WmiObject -Class win32_volume | ?{$_.Name -match "\\\\?\\";}

PS C:\Windows\system32> Format-Volume -DriveLetter $driveLetter -FileSystem NTFS -AllocationUnitSize $sectorSize -NewFil
eSystemLabel $diskLabel -Confirm:$false -Force

DriveLetter FileSystemLabel FileSystem DriveType HealthStatus OperationalStatus SizeRemaining Size
----------- --------------- ---------- --------- ------------ ----------------- ------------- ----
F SIMONICULA-007 NTFS Fixed Healthy OK 12 TB 12 TB

Errors:

Add-ClusterDisk : The disk with Id {1}\\TestCluster007\root/Microsoft/Windows/Storage/Providers_v2\WSP_Disk.ObjectId="{4ef8972
8-9924-414d-960a-f09d2ab2155a}:DI:\\?\Disk{6fca7305-68a9-5804-84ed-bf26bd6bfc62}" is unable to be clustered.
At line:1 char:22
+ Get-Disk -Number 7 | Add-ClusterDisk | %{$_.name="PRDSHOME40-41" }
+ ~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (MSFT_Disk (Obje...Windows/Sto...):CimInstance) [Add-ClusterDisk], Invalid
OperationException
+ FullyQualifiedErrorId : Add-ClusterDisk,Microsoft.FailoverClusters.PowerShell.AddClusterDiskCommand

Resolution:
- Delete the LUNs at the SAN Volumes Manager
- Online disk > partition GPT > format disk as NTFS > assign drive letter > add disk to Cluster

Miscellaneous clustering commands:
# $clusterDiskObject=Get-ClusterResource -Cluster $cluster -Name $clusterDiskName
# Put disk in cluster maintenance mode
# $clusterDiskObject | Suspend-ClusterResource
# $clusterDiskObject | Stop-ClusterResource
<# avoid this error:
Format-Volume : The specified object is managed by the Microsoft Failover Clustering component. The disk must be in
cluster maintenance mode and the cluster resource status must be online to perform this operation.

#Clear-ClusterDiskReservation -Disk $diskNumber
#$clusterDiskObject | Start-ClusterResource | Resume-ClusterResource
# Put clustered disk in maintenance mode
# Get volume
# get-partition -DriveLetter C
#New-FileShare -Name $shareName -FileServerFriendlyName $serverName -SourceVolume $Volume -IsContinuouslyAvailable $True -Protocol SMB

# Set-Partition -DriveLetter $driveLetter -IsActive $true

# Create report after volumes have been created
Get-WmiObject -Class win32_volume | select name,blocksize

Error:
PS C:\Windows\system32> Set-Partition -DriveLetter $driveLetter -IsActive $true
Set-Partition : A parameter is not valid for this type of partition.

Extended information:
The parameters MbrType and IsActive cannot be used on a GPT disk.

Activity ID: {9b225291-2b3f-43e5-b62f-74a96d8e5dd6}
At line:1 char:1
+ Set-Partition -DriveLetter $driveLetter -IsActive $true
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidArgument: (StorageWMI:ROOT/Microsoft/..._StorageCmdlets) [Set-Partition], CimExce
ption
+ FullyQualifiedErrorId : StorageWMI 41006,Set-Partition

Resolution:
The command only works with MBR disks - cannot issue it against at GTP type.
#>


################################## Excuting Program as an Administrator ####################################
# Get the ID and security principal of the current user account
$myWindowsID=[System.Security.Principal.WindowsIdentity]::GetCurrent()
$myWindowsPrincipal=new-object System.Security.Principal.WindowsPrincipal($myWindowsID)

# Get the security principal for the Administrator role
$adminRole=[System.Security.Principal.WindowsBuiltInRole]::Administrator

# Check to see if we are currently running "as Administrator"
if ($myWindowsPrincipal.IsInRole($adminRole))
{
# We are running "as Administrator" - so change the title and background color to indicate this
$Host.UI.RawUI.WindowTitle = $myInvocation.MyCommand.Definition + "(Elevated)"
$Host.UI.RawUI.BackgroundColor = "Black"
clear-host
}
else
{
# We are not running "as Administrator" - so relaunch as administrator

# Create a new process object that starts PowerShell
$newProcess = new-object System.Diagnostics.ProcessStartInfo "PowerShell";

# Specify the current script path and name as a parameter
$newProcess.Arguments = $myInvocation.MyCommand.Definition;

# Indicate that the process should be elevated
$newProcess.Verb = "runas";

# Start the new process
[System.Diagnostics.Process]::Start($newProcess);

# Exit from the current, unelevated, process
exit
}

Write-Host -NoNewLine "Running as Administrator..."
################################## Excuting Program as an Administrator ####################################

# Set global variables
$sectorSize=16384 #16K which translates to 64TB max per volume
$driveLettersExclusion="[ABCDHKLMPZ]"
$serverName="SHERVER007"
$cluster="CLUSTER007"

# Set cluster disk label and disk number
$clusterDisks=@(
@("DISK-008",8),
@("DISK-009",9),
@("DISK-010",10),
@("DISK-011",11),
@("DISK-012",12),
@("DISK-013",13),
@("DISK-014",14),
@("DISK-015",15)
)

# Scan for next available drive letter, excluding D "CD Rom" and H "Home"
$availableDriveLetters=ls function:[A-Z]: -n|?{!(test-path $_)}|%{$_[0]}|?{!($_ -match $driveLettersExclusion)}

function createVolume($name,$number,$letter){
# Set variables
$diskNumber=$number
$diskLabel=$name
$driveLetter=$letter
$drivePath=$driveLetter+":"
$tempDriveLetter=$availableDriveLetters[$availableDriveLetters.Length -1]
$tempPath=$tempDriveLetter+":"

"Processing disk number $diskNumber`: set drive letter as $driveLetter & label equals $diskLabel"

# specify disk object
# $disk = Get-Disk -Number $diskNumber

# Set disk as online
Set-Disk $diskNumber -IsOffline $false -InformationAction SilentlyContinue # This PowerShell command doesn't work

# Clean disk
Clear-Disk -Number $diskNumber -RemoveData -Confirm:$False

# Set partition as GPT
Initialize-Disk -Number $diskNumber -PartitionStyle GPT -InformationAction SilentlyContinue

# Create a new partition and format it
#Add-PartitionAccessPath -DiskNumber $diskNumber -PartitionNumber 2 -AccessPath $drivePath
New-Partition $diskNumber -UseMaximumSize -DriveLetter $tempDriveLetter
Format-Volume -DriveLetter $tempDriveLetter -FileSystem NTFS -AllocationUnitSize $sectorSize -NewFileSystemLabel $diskLabel -Confirm:$false -Force
Set-Disk $diskNumber -isOffline $false
Set-Disk $diskNumber -isReadOnly $false

# Use WMI to relabel volumes as PowerShell currently doesn't have this capability
$disk = Get-WmiObject -Class win32_volume -Filter "Label = '$diskLabel'"
Set-WmiInstance -input $disk -Arguments @{DriveLetter=$drivePath; Label="$diskLabel"}

Remove-PartitionAccessPath -DiskNumber $diskNumber -PartitionNumber 2 -Accesspath $tempPath
}

function addDiskToCluster($name,$number){
Get-Disk -Number $number | Add-ClusterDisk | %{$_.name=$name }
}

function processDisks{

$i=0;
foreach ($disk in $clusterDisks){
createVolume $($disk[0]) $($disk[1]) $availableDriveLetters[$i++];
#pause;
}

"Please verify that all disks have been created prior to proceeding to add disks to Clusters."
pause;

$clusterDisks | %{
"Please verify the accuracy of this statement and press Enter to accept:`r`n"
"Name: $($_[0]) | Localhost Physical Disk Number: $($_[1])";
#pause;
addDiskToCluster $($_[0]) $($_[1]);
}
}

processDisks;
Sample Output:
Running as Administrator...Processing disk number 8: set drive letter as Q & label equals DISK-008

DiskPath: \\?\Disk{13efa5dc-bfb7-4267-4b38-a7bfc5755a61}

PartitionNumber DriveLetter Offset Size Type
--------------- ----------- ------ ---- ----
2 Y 135266304 74.87 GB Basic

ObjectId : {1}\\CLUSTER1\root/Microsoft/Windows/Storage/Providers_v2\WSP_Volume.ObjectId="{4ef89728-9924-414d-960a-f09d2ab2155a}:VO:\\?\V
olume{6b96c857-e7e7-41d9-ad49-c83be3c7ffcc}\"
PassThroughClass :
PassThroughIds :
PassThroughNamespace :
PassThroughServer :
UniqueId : \\?\Volume{6b96c857-e7e7-41d9-ad49-c83be3c7ffcc}\
AllocationUnitSize : 16384
DedupMode : NotAvailable
DriveLetter : Y
DriveType : Fixed
FileSystem : NTFS
FileSystemLabel : DISK-008
FileSystemType : NTFS
HealthStatus : Healthy
OperationalStatus : OK
Path : \\?\Volume{6b96c857-e7e7-41d9-ad49-c83be3c7ffcc}\
Size : 80394305536
SizeRemaining : 79481651200
PSComputerName :


__GENUS : 2
__CLASS : Win32_Volume
__SUPERCLASS : CIM_StorageVolume
__DYNASTY : CIM_ManagedSystemElement
__RELPATH : Win32_Volume.DeviceID="\\\\?\\Volume{6b96c857-e7e7-41d9-ad49-c83be3c7ffcc}\\"
__PROPERTY_COUNT : 44
__DERIVATION : {CIM_StorageVolume, CIM_StorageExtent, CIM_LogicalDevice, CIM_LogicalElement...}
__SERVER : SHERVER007
__NAMESPACE : root\cimv2
__PATH : \\SHERVER007\root\cimv2:Win32_Volume.DeviceID="\\\\?\\Volume{6b96c857-e7e7-41d9-ad49-c83be3c7ffcc}\\"
Access :
Automount : True
Availability :
BlockSize : 16384
BootVolume : False
Capacity : 80394305536
Caption : Q:\
Compressed : False
ConfigManagerErrorCode :
ConfigManagerUserConfig :
CreationClassName :
Description :
DeviceID : \\?\Volume{6b96c857-e7e7-41d9-ad49-c83be3c7ffcc}\
DirtyBitSet : False
DriveLetter : Q:
DriveType : 3
ErrorCleared :
ErrorDescription :
ErrorMethodology :
FileSystem : NTFS
FreeSpace : 79481667584
IndexingEnabled : True
InstallDate :
Label : DISK-008
LastErrorCode :
MaximumFileNameLength : 255
Name : Q:\
NumberOfBlocks :
PageFilePresent : False
PNPDeviceID :
PowerManagementCapabilities :
PowerManagementSupported :
Purpose :
QuotasEnabled : False
QuotasIncomplete : False
QuotasRebuilding : False
SerialNumber : 2190170871
Status :
StatusInfo :
SupportsDiskQuotas : True
SupportsFileBasedCompression : False
SystemCreationClassName :
SystemName : SHERVER007
SystemVolume : False
PSComputerName : SHERVER007

Press Enter to continue...:

### Truncated for brevity ###

Please verify that all disks have been created prior to proceeding to add disks to Clusters.
Press Enter to continue...:
Please verify the accuracy of this statement and press Enter to accept:

Name: DISK-008 | Localhost Physical Disk Number: 8
Press Enter to continue...:
Please verify the accuracy of this statement and press Enter to accept:

### Truncated for brevity ###

Name: DISK-015 | Localhost Physical Disk Number: 15
Press Enter to continue...: