Windows Server 2022 Storage Spaces Direct Configuration Guide

Tyler Maginnis | January 25, 2024

Windows Server 2022Storage Spaces DirectS2DSoftware-Defined Storage

Need Professional Windows Server 2022?

Get expert assistance with your windows server 2022 implementation and management. Tyler on Tech Louisville provides priority support for Louisville businesses.

Same-day service available for Louisville area

Windows Server 2022 Storage Spaces Direct (S2D) Configuration Guide

Introduction

Storage Spaces Direct (S2D) is a software-defined storage feature in Windows Server 2022 that enables building highly available and scalable storage systems using industry-standard servers with local-attached drives. This guide provides comprehensive instructions for planning, deploying, and managing Storage Spaces Direct.

Prerequisites

Hardware Requirements

  • Minimum 2 servers, maximum 16 servers per cluster
  • Identical or similar server configurations
  • Minimum 4 drives per server (excluding boot drives)
  • 10 GbE or faster network adapters (RDMA recommended)
  • Separate networks for management and storage traffic

Software Requirements

  • Windows Server 2022 Datacenter Edition
  • Latest Windows updates installed
  • Active Directory domain membership
  • Administrator privileges

Supported Drive Types

  • NVMe (recommended for cache)
  • SSD SATA/SAS (cache or capacity)
  • HDD SATA/SAS (capacity only)

1. Pre-Deployment Validation

Hardware Inventory

# Check server hardware configuration
Get-PhysicalDisk | Select-Object FriendlyName, MediaType, Size, HealthStatus
Get-NetAdapter | Where-Object {$_.Status -eq "Up"} | Select-Object Name, InterfaceDescription, LinkSpeed
Get-WmiObject Win32_ComputerSystem | Select-Object Manufacturer, Model, TotalPhysicalMemory

Network Validation

# Test RDMA capability
Get-NetAdapterRdma | Select-Object Name, Enabled, RdmaCapabilities

# Test network connectivity between nodes
Test-Cluster -Node server1,server2,server3,server4 -Include "Network"

# Verify jumbo frames
Get-NetAdapterAdvancedProperty -Name * | Where-Object {$_.DisplayName -match "Jumbo"} | Select-Object Name, DisplayName, DisplayValue

2. Initial Configuration

Configure Network

# Configure management network
New-NetIPAddress -InterfaceAlias "Management" -IPAddress 192.168.1.10 -PrefixLength 24 -DefaultGateway 192.168.1.1
Set-DnsClientServerAddress -InterfaceAlias "Management" -ServerAddresses 192.168.1.5,192.168.1.6

# Configure storage networks (2 for redundancy)
New-NetIPAddress -InterfaceAlias "Storage1" -IPAddress 10.10.10.10 -PrefixLength 24
New-NetIPAddress -InterfaceAlias "Storage2" -IPAddress 10.10.20.10 -PrefixLength 24

# Enable jumbo frames
Set-NetAdapterAdvancedProperty -Name "Storage1" -DisplayName "Jumbo Frame" -DisplayValue "9014"
Set-NetAdapterAdvancedProperty -Name "Storage2" -DisplayName "Jumbo Frame" -DisplayValue "9014"

# Configure RDMA
Enable-NetAdapterRdma -Name "Storage1","Storage2"
Set-NetAdapterRdma -Name "Storage1","Storage2" -RdmaMode iWARP

Install Required Features

# Install on all servers
$servers = @("Server1", "Server2", "Server3", "Server4")
Invoke-Command -ComputerName $servers -ScriptBlock {
    Install-WindowsFeature -Name Hyper-V, Failover-Clustering, Data-Deduplication, BitLocker, File-Server, RSAT-Clustering-PowerShell, Hyper-V-PowerShell -IncludeManagementTools -Restart
}

3. Create Failover Cluster

Validate Cluster Configuration

# Run comprehensive cluster validation
Test-Cluster -Node Server1,Server2,Server3,Server4 -Include "Storage Spaces Direct", Inventory, Network, "System Configuration"

Create Cluster

# Create new cluster
New-Cluster -Name S2DCluster -Node Server1,Server2,Server3,Server4 -StaticAddress 192.168.1.100 -NoStorage

# Configure cluster network names
(Get-ClusterNetwork -Name "Cluster Network 1").Name = "Management"
(Get-ClusterNetwork -Name "Cluster Network 2").Name = "Storage1"
(Get-ClusterNetwork -Name "Cluster Network 3").Name = "Storage2"

# Configure cluster network roles
(Get-ClusterNetwork -Name "Management").Role = 3  # Cluster + Client
(Get-ClusterNetwork -Name "Storage1").Role = 1    # Cluster Only
(Get-ClusterNetwork -Name "Storage2").Role = 1    # Cluster Only

Configure Cluster Quorum

# Configure cloud witness
Set-ClusterQuorum -CloudWitness -AccountName "s2dwitness" -AccessKey "your-storage-account-key"

# Or configure file share witness
Set-ClusterQuorum -FileShareWitness "\\fileserver\witness$"

4. Enable Storage Spaces Direct

Clean Drives

# Clean all drives on all nodes (WARNING: This will erase all data)
Invoke-Command -ComputerName $servers -ScriptBlock {
    Get-PhysicalDisk | Where-Object {$_.CanPool -eq $true} | Reset-PhysicalDisk
    Get-Disk | Where-Object {$_.Number -ne 0 -and $_.IsBoot -ne $true} | Set-Disk -IsOffline $false
    Get-Disk | Where-Object {$_.Number -ne 0 -and $_.IsBoot -ne $true} | Set-Disk -IsReadOnly $false
    Get-Disk | Where-Object {$_.Number -ne 0 -and $_.IsBoot -ne $true} | Clear-Disk -RemoveData -RemoveOEM -Confirm:$false
}

Enable S2D

# Enable Storage Spaces Direct
Enable-ClusterStorageSpacesDirect -PoolFriendlyName "S2DPool" -Confirm:$false

# Verify S2D is enabled
Get-ClusterStorageSpacesDirect

5. Storage Configuration

Create Volumes

# Create mirror volume (2-way or 3-way)
New-Volume -StoragePoolFriendlyName "S2DPool" -FriendlyName "Mirror-Volume" -Size 1TB -ResiliencySettingName Mirror -ProvisioningType Thin

# Create parity volume (for cold data)
New-Volume -StoragePoolFriendlyName "S2DPool" -FriendlyName "Parity-Volume" -Size 5TB -ResiliencySettingName Parity -ProvisioningType Thin

# Create tiered volume (mirror + parity)
New-StorageTier -StoragePoolFriendlyName "S2DPool" -FriendlyName "Performance" -MediaType SSD -ResiliencySettingName Mirror
New-StorageTier -StoragePoolFriendlyName "S2DPool" -FriendlyName "Capacity" -MediaType HDD -ResiliencySettingName Parity

New-Volume -StoragePoolFriendlyName "S2DPool" -FriendlyName "Tiered-Volume" -StorageTierFriendlyNames "Performance", "Capacity" -StorageTierSizes 200GB, 800GB

Configure CSV (Cluster Shared Volumes)

# Add volumes to CSV
Get-ClusterSharedVolume
Add-ClusterSharedVolume -Name "Cluster Virtual Disk (Mirror-Volume)"
Add-ClusterSharedVolume -Name "Cluster Virtual Disk (Parity-Volume)"

# Rename CSV mount points
$csv = Get-ClusterSharedVolume -Name "Cluster Virtual Disk (Mirror-Volume)"
$csv.Name = "CSV-Mirror"

6. Performance Optimization

Configure Storage QoS

# Create QoS policies
New-StorageQosPolicy -Name "Gold" -PolicyType Dedicated -MinimumIops 1000 -MaximumIops 5000
New-StorageQosPolicy -Name "Silver" -PolicyType Dedicated -MinimumIops 500 -MaximumIops 2500
New-StorageQosPolicy -Name "Bronze" -PolicyType Dedicated -MinimumIops 100 -MaximumIops 1000

# Apply QoS to volumes
Get-Volume -FriendlyName "Mirror-Volume" | Set-StorageQosPolicy -Name "Gold"

Configure Cache

# View cache configuration
Get-ClusterStorageSpacesDirect | Select-Object CacheState, CacheMetadataReserveBytes, CachePageSizeKBytes

# Modify cache settings (requires restart of S2D)
Set-ClusterStorageSpacesDirect -CacheModeSSD ReadWrite -CacheModeHDD ReadOnly

Enable Deduplication

# Enable dedup on volume
Enable-DedupVolume -Volume "C:\ClusterStorage\Volume1" -UsageType HyperV

# Configure dedup schedule
Set-DedupSchedule -Name "BackgroundOptimization" -Start 22:00 -DurationHours 6 -Days Monday,Tuesday,Wednesday,Thursday,Friday

7. Fault Tolerance Configuration

Configure Fault Domains

# Create fault domains for rack awareness
New-ClusterFaultDomain -Type Rack -Name "Rack1"
New-ClusterFaultDomain -Type Rack -Name "Rack2"

# Assign nodes to racks
Set-ClusterFaultDomain -Name "Server1" -Parent "Rack1"
Set-ClusterFaultDomain -Name "Server2" -Parent "Rack1"
Set-ClusterFaultDomain -Name "Server3" -Parent "Rack2"
Set-ClusterFaultDomain -Name "Server4" -Parent "Rack2"

# View fault domain configuration
Get-ClusterFaultDomain

Configure Storage Resiliency

# Set resiliency period
(Get-Cluster).ResiliencyDefaultPeriod = 240  # 4 minutes

# Configure storage repair
Set-StoragePool -FriendlyName "S2DPool" -RepairPolicy Parallel -RetireMissingPhysicalDisks Always

8. Monitoring and Health

Health Service Configuration

# View health service status
Get-ClusterResource "Health Service" | Get-ClusterParameter

# Configure health service settings
Get-ClusterResource "Health Service" | Set-ClusterParameter -Name "Enable" -Value 1

Performance Monitoring

# Create performance baseline
$counters = @(
    "\Cluster Storage Hybrid Disk(*)\*",
    "\Cluster Storage Cache Stores(*)\*",
    "\Storage Spaces Direct\*",
    "\PhysicalDisk(*)\*"
)

# Create data collector set
$datacollector = New-Object -ComObject Pla.DataCollectorSet
$datacollector.DisplayName = "S2D Performance"
$datacollector.Duration = 300
$datacollector.SubdirectoryFormat = 1
$datacollector.RootPath = "C:\PerfLogs\S2D"

$collector = $datacollector.DataCollectors.CreateDataCollector(0)
$collector.FileName = "S2D_Performance"
$collector.FileNameFormat = 0
$collector.PerformanceCounters = $counters
$datacollector.DataCollectors.Add($collector)
$datacollector.Commit("S2D Performance", $null, 0x0003)

Storage Health Monitoring

# Check storage subsystem health
Get-StorageSubsystem -FriendlyName "*Clustered*" | Get-StorageHealthReport

# Monitor storage jobs
Get-StorageJob

# Check disk health
Get-PhysicalDisk | Select-Object FriendlyName, HealthStatus, OperationalStatus, Size, MediaType

# View storage pool health
Get-StoragePool "S2DPool" | Get-StorageHealthReport

9. Maintenance Operations

Pause and Drain Node

# Pause node for maintenance
Suspend-ClusterNode -Name "Server1" -Drain

# Resume node after maintenance
Resume-ClusterNode -Name "Server1"

# Move all resources off a node
Move-ClusterGroup -Node "Server2"
Move-ClusterSharedVolume -Node "Server2"

Drive Replacement

# Identify failed drive
Get-PhysicalDisk | Where-Object {$_.HealthStatus -ne "Healthy"}

# Retire failed drive
$disk = Get-PhysicalDisk -FriendlyName "PhysicalDisk-1"
$disk | Set-PhysicalDisk -Usage Retired

# After physical replacement, add new drive to pool
$newDisk = Get-PhysicalDisk | Where-Object {$_.CanPool -eq $true}
Add-PhysicalDisk -StoragePoolFriendlyName "S2DPool" -PhysicalDisks $newDisk

Storage Repair

# Check repair status
Get-StoragePool "S2DPool" | Get-StorageHealthReport

# Trigger storage repair
Repair-VirtualDisk -FriendlyName "Mirror-Volume"

# Monitor repair progress
Get-StorageJob | Where-Object {$_.Name -like "*repair*"}

10. Scaling Operations

Add Node to Cluster

# Prepare new node
Install-WindowsFeature -Name Hyper-V, Failover-Clustering, Data-Deduplication -IncludeManagementTools

# Add node to cluster
Add-ClusterNode -Name "Server5" -Cluster "S2DCluster"

# Rebalance storage
Optimize-StoragePool -FriendlyName "S2DPool"

Add Capacity

# Add new drives to existing nodes
Get-PhysicalDisk | Where-Object {$_.CanPool -eq $true} | Add-PhysicalDisk -StoragePoolFriendlyName "S2DPool"

# Optimize storage pool
Optimize-StoragePool -FriendlyName "S2DPool"

11. Backup and Disaster Recovery

Configure Backup

# Install Windows Server Backup
Install-WindowsFeature -Name Windows-Server-Backup

# Create backup policy for CSV
$policy = New-WBPolicy
$volume = Get-WBVolume -VolumePath "C:\ClusterStorage\Volume1"
Add-WBVolume -Policy $policy -Volume $volume

# Set backup target
$target = New-WBBackupTarget -NetworkPath "\\backup-server\S2D-Backups"
Add-WBBackupTarget -Policy $policy -Target $target

# Schedule backup
Set-WBSchedule -Policy $policy -Schedule 02:00
Set-WBPolicy -Policy $policy

Storage Replica Configuration

# Configure Storage Replica for DR
New-SRPartnership -SourceComputerName "S2DCluster" -SourceRGName "RG01" -SourceVolumeName "C:\ClusterStorage\Volume1" -DestinationComputerName "DRCluster" -DestinationRGName "RG02" -DestinationVolumeName "C:\ClusterStorage\Volume1" -LogVolumeName "L:" -LogSizeInBytes 8GB

12. Troubleshooting

Common Diagnostics

# Check cluster logs
Get-ClusterLog -Destination C:\ClusterLogs

# View storage spaces direct events
Get-WinEvent -LogName Microsoft-Windows-StorageSpacesDirect*/Operational | Select-Object -First 50

# Test storage spaces direct
Test-Cluster -Include "Storage Spaces Direct"

# Check storage communication
Get-ClusterPerformanceHistory -ObjectType StorageSubsystem -TimeFrame LastHour

Performance Troubleshooting

# Check cache hit ratio
Get-ClusterPerformanceHistory -ObjectType Volume -TimeFrame LastHour | Where-Object {$_.MetricId -eq "Volume.Cache.HitRate"}

# Monitor IOPS and latency
Get-ClusterPerformanceHistory -ObjectType Volume -TimeFrame LastHour | Where-Object {$_.MetricId -match "IOPS|Latency"}

# Check network utilization
Get-ClusterPerformanceHistory -ObjectType NetAdapter -TimeFrame LastHour

Best Practices

  1. Hardware
  2. Use identical hardware across all nodes
  3. Use enterprise-grade SSDs for cache
  4. Implement proper cooling for NVMe drives
  5. Use RDMA-capable network adapters

  6. Network

  7. Dedicate networks for storage traffic
  8. Enable jumbo frames on storage networks
  9. Use RDMA for better performance
  10. Implement network redundancy

  11. Configuration

  12. Keep firmware and drivers updated
  13. Use thin provisioning for flexibility
  14. Configure appropriate resiliency settings
  15. Plan for 20-30% free space in pool

  16. Maintenance

  17. Perform regular health checks
  18. Monitor performance baselines
  19. Test backup and restore procedures
  20. Document configuration changes

  21. Performance

  22. Size cache tier appropriately (10% minimum)
  23. Use tiered storage for mixed workloads
  24. Enable deduplication for suitable workloads
  25. Monitor and adjust QoS policies

Additional Resources