Manage Windows Server storage with PowerShell
Robots in the Data Center
Experienced Windows administrators are familiar with numerous tools that help them manage hard drives, partitions, and filesystems. The portfolio extends from the Microsoft Management Console (MMC)-based Disk Management (diskmgmt.msc
) and command-line tools to the Server Manager and Windows Admin Center. For more complex storage systems, you have tools such as iSCSICLI for configuring built-in Internet small computer systems interface (iSCSI) initiators or the multipath I/O control panel (MPIO CPL
) graphical configuration applet for storage multipathing.
These tools are perfectly suitable for individual tasks, but when it comes to configuring a large number of servers or migrating complex storage landscapes, the tools quickly reach their limits. The syntax is not consistent, output depends on the operating system language set, and graphical interfaces do not support automation. In precisely such cases administrators turn their attention to PowerShell, which is also particularly important because it enables visual management of a Windows machine's local subsystems in Windows Admin Center.
This article references the current Server 2022 but should also apply to Server 2019 and 2016 without any changes. However, I look exclusively at provisioning storage capacity, which is served up by Server Message Block (SMB), Distributed File System (DFS), Network File System (NFS), or iSCSI supported by separate, sophisticated PowerShell features.
Physical and Logical Drives
Classical administration tasks with regard to the hard drives, their partitioning, and the storage volumes on them are traditionally the domain of the Disk Management MMC console or the diskpart
command-line tool. The Get-Disk
command in PowerShell is useful for an initial overview of the existing hard drives (Figure 1). The returned objects correspond to the view in Disk Management as long as all existing drives are of Basic Disk
type. If you convert a disk to a Dynamic Disk
, it disappears from the Get-Disk
inventory, but remains visible in Disk Management.
In fact, PowerShell does not support operations that require dynamic disks (the Windows software RAID in various flavors). If you want to script dynamic disks, you will probably have to resort to diskpart
, because neither PowerShell nor Windows Management Instrumentation (WMI) provide documented programming interfaces. However, dynamic disks are a little out of fashion: Microsoft explicitly recommends Storage Spaces [1] for mirrored startup volumes, and Storage Spaces is far better suited for more advanced configurations of local hard drives, with extensive PowerShell support, which I look at later.
To initialize a fresh hard drive for operation, use:
Get-Disk -Number <drive number> | Set-Disk -IsOffline $false Initialize-Disk -Number <drive number> -PartitionStyle GPT
For the same initialization type for all uninitialized disks use:
Get-Disk | Where-Object {$_.IsOffline -and ($_.PartitionStyle -eq 'RAW')} | Initialize-Disk -PartitionStyle GPT
To continue processing the disk objects after initialization, you also need to add the -PassThru
switch to the Initialize-Disk
cmdlet.
To take an active hard drive offline again, use:
Get-Disk -Number <drive number> | Set-Disk -IsOffline $true
If you are working with real, not logical, disks physically connected to the computer, all of the tools I have mentioned so far will quickly reach their limits. The Get-Disk
cmdlet does not display dynamic disks, and none of the three tools return disks that have been added to a Storage Spaces pool. Conventional Windows on-board tools let you see the connected disks in Device Management (devmgmt.msc
), where you also have access to the multipath I/O information of the respective disk if configured for multiple paths.
PowerShell helps you achieve this functionality with the Get-PhysicalDisk
command, but it does not show you any multipathing information. Moreover, the physical connection information relating to the adapter, target, and logical unit number (LUN) is only available as a string in the PhysicalLocation
property. Unfortunately, this is exclusively for local drives and not for disks provided by a storage system by iSCSI or Fibre Channel (FC). The command
diskpart detail disk
shows this information – unless the disks are part of a Storage Spaces pool. With the same preconditions, you can retrieve this information in PowerShell with WMI:
Get-CIMInstance Win32_DiskDrive | Select Index, Model, SCSI*
The Index
corresponds to the disk number in Get-Disk
.
If the storage configuration changes outside of your PowerShell session, the PowerShell cmdlets don't always notice it right away. You are probably also familiar with this phenomenon from Disk Management and would select the Action | Rescan Disks
menu item to fix it. In PowerShell, the equivalent command is Update-HostStorageCache
.
Managing Partitions and Volumes
Managing partitions, and the filesystem volumes on them, is fraught with the same limitations as hard drive management. Dynamic volumes are invisible to PowerShell; however, you can use the Get-Partition
cmdlet to list all partitions on all visible disks (i.e., Basic Disks and Storage Spaces VirtualDisks) without having to commit to a specific disk beforehand. What's even better is how you can manage volumes: Everything is visible and manageable regardless of the underlying storage technology. You can list them with Get-Volume
, repair them with Repair-Volume
, analyze and defragment them with Optimize-Volume
, and reformat them with the Format-Volume
command. The default setting is Quick Format
, but you can force full formatting with the -Full
switch. When creating a new volume, you can either use the entire disk:
Get-Disk -Number <number> | New-Volume -FileSystem NTFS -DriveLetter Z -FriendlyName <DiskLabel>
or create partitions:
Get-Disk -Number <number> | New-Partition -Size <size in bytes> -Offset <offset in bytes>
When done, create and format the filesystem volume with:
$part = Get-Partition -DiskNumber <number> -PartitionNumber <number> $part | Set-Partition -NewDriveLetter <letter> Format-Volume -Partition $part -FileSystem NTFS
You can clearly see the connections between volumes and partitions in Windows in the last example. The partition determines the physical location of the object on the disk subsystem and its deployment, and the volume defines the filesystem therein.
Multipath Mounting of iSCSI LUNs
As early as in Windows 2000 Server, the Microsoft operating system introduced its own iSCSI initiator that let you mount storage LUNs on your Windows instances with on-board tools – provided the LUNs were provisioned by the storage system with the iSCSI protocol. MPIO is another valuable feature that is available not only for iSCSI but for all block-based storage protocols, such as serial attached SCSI (SAS), FC, or Fibre Channel over Ethernet (FCoE). MPIO lets the initiator manage multiple connections to the same storage target in line with a defined policy.
On a freshly installed Windows system, the iSCSI initiator service (MSiSCSI) is disabled by default. Before configuring the initiator, you first need to configure and start the service for automatic startup with two PowerShell commands:
Set-Service MSiSCSI -StartupType Automatic Start-Service MSiSCSI
Now you can configure the iSCSI initiator on your Windows server. One of the most important global settings is the iSCSI qualified name (IQN) of the initiator. To adjust this, use:
Set-InitiatorPort -NodeAddress <IQN> -NewNodeAddress <New IQN>
To find the existing initiator name, enter:
Get-InitiatorPort -ConnectionType iSCSI
Changes to the initiator name are often caused by the storage team's naming conventions. However, compelling technical reasons might also be in play. The IQN of a Windows computer assigned by default is a 26-character prefix followed by the computer name. However, older storage systems internally limit the IQN to 32 characters and therefore only evaluate the first six characters of the computer name. For example, if your cluster nodes are named SERVER01, SERVER02, and so on, this kind of storage system will see them all as SERVER, and LUN reservations within the cluster will fail.
Sometimes iSCSI connections take too long to establish when the server boots, and shares that reside on iSCSI LUNs are not provisioned. In this case, you need to make the server service (LanmanServer) dependent on the iSCSI initiator service (MSiSCSI). Creating this dependency is not possible with PowerShell tools; instead, you need to turn to the tried and trusted sc config depend
. Make sure you add the iSCSI service and do not set it as the only dependency.
If multipathing with your current or future storage system is an option, it is best to configure the MPIO feature right away. In fact, for server clusters, it is mandatory at least to enable MPIO before creating the cluster. The MPIO feature has built-in support for the on-board iSCSI initiator. However, this is disabled by default. Enabling this support requires a reboot, so you will want to complete this configuration step as early as possible:
Add-WindowsFeature Multipath-IO $vid = 'MSFT2005' $pid = 'iSCSIBusType_0x9' New-MSDSMSupportedHW -VendorId $vid -ProductId $pid Set-MSDSMGlobalDefaultLoadBalance-Policy -Policy RR Restart-Computer -Force
The last configuration command sets the path selection policy to round robin (RR
) by default. Each subsequent request takes the next active path regardless of the workload. Other valid values for the MPIO policy are FOO
for failover only, None
for no multipathing, LB
for least blocks, and LQD
for least queue depth. Make sure you read the storage vendor's integration guide before choosing an MPIO policy because not every iSCSI storage system supports every policy equally well. In most cases you will only have the choice between round robin and failover. The Get-MPIOSetting
and Set-MPIOSetting
commands let you configure further global MPIO settings on the system.
Once the iSCSI initiator is enabled and MPIO is configured, you can attach your iSCSI storage to the system and read the LUNs presented to your initiator. (For the sake of brevity, I will not look at authenticating against the target storage system here.) The parameters required are provided by Connect-IscsiTarget
[2]. Listing 1 shows how to connect an iSCSI target to two paths with PowerShell. Make sure that the IP addresses from the individual storage subnets appear in the same order in the arrays for the initiator and target addresses.
Listing 1
Connecting to an iSCSI Target
$iniAddr = @( '10.0.104.11' '10.0.105.11' ) $tgtAddr = @( '10.0.104.21' '10.0.105.21' ) (0..($iniAddr.Length - 1)) | For-Each-Object { New-IscsiTargetPortal ` -TargetPortalAddress $tgtAddr[$_] ` -TargetPortalPortNumber 3260 ` -InitiatorPortalAddress $iniAddr[$_] } $targets = Get-IscsiTarget (0..($iniAddr.Length - 1)) | For-Each-Object { $targets | Connect-IscsiTarget ` -IsMultipathEnabled $true ` -IsPersistent $true ` -TargetPortalAddress $tgtAddr[$_] ` -InitiatorPortalAddress $iniAddr[$_] }
Now, to load the newly attached iSCSI targets in Disk Management, use
Update-Host-StorageCache
New disks will now show up in your storage inventory, assuming the storage system has served up LUNs to this initiator.
The supplied Windows PowerShell modules do not provide any commands for advanced management of the MPIO settings of the individual devices and paths. WMI support in this area is also poor and only gives you read access to the MPIO configuration of the individual devices; you cannot change the configuration. You can either turn to the mpclaim.exe
tool on the command line, with its slightly cryptic syntax, or configure the settings manually in the Device Management GUI (Figure 2).
Buy this article as PDF
(incl. VAT)