Select Page

Monitor Storage Spaces Direct Pool Capacity with PowerShell

This article provides PowerShell code and examples to analyze Windows Storage Spaces Direct pools and returns the current storage capacity, free space and other values including the amount of free space required to automatically rebuild a failed disk.

Purpose

Monitoring Storage Spaces pools for capacity and available free space is simple enough with Windows Server Manager. Selecting the Storage Pool properties provides a graphical report.

Retrieving these values from PowerShell takes a bit more work. What is required for this operation is to:

  1. Select the Storage Pool
  2. Retrieve every disk within this pool
  3. Sum up all the capacity and free space on each disk

A trickier calculation is to evaluate if enough un-allocated space is available to survive a disk failure and automatically rebuild the storage pool to map around the missing disk. This is even more complex when there are multiple disk media types within your pool. An SSD failure can only be re-mapped with other SSD drives.

Get-ClusterS2DStorageUsage

The CmdLet included in this article provides a simple command to analyze pools and programmatically return pool statistics.

Command Syntax:

Get-ClusterS2DStorageUsage [-StoragePool string[ ] [-MediaType SSD|HDD|SCM]

Parameters

-StoragePool

Storage Pool friendly name. If not included all non-primordial storage pools will be processed.

-MediaType

Selects specific disk drive media types for processing. If not included, all non-Journal media types processed.

Example 1: Basic output

Get-ClusterS2DStorageUsage

Pool              : S2DPool
TotalCapacity     : 120
TotalUsedSpace    : 24
TotalFreeSpace    : 96
PercentUsed       : 20
UnitOfMeasure     : GB
DiskCount         : 24
SelectedMediaType : ALL
ReserveRequired   : 5

Example 2: Disk media type selected

Get-ClusterS2DStorageUsage -MediaType HDD | ft Pool, DiskCount, TotalCapacity, TotalFreeSpace, ReserveRequired

Pool        DiskCount     TotalCapacity       TotalFreeSpace      ReserveRequired
----        ----------    ------------        --------------      ---------------
S2DPool     16            80                  64                  5

The results of this command show the S2DPool has 16 HDD disks with 64GB free space and required 5GB free space to rebuild.

Example 3: Verbose output

Get-ClusterS2DStorageUsage -Verbose

VERBOSE: Disk Name: [Msft Virtual Disk] Size: 5.00 GB Alloc: 1.00 GB Free Space: 4.00 GB
VERBOSE: Disk Name: [Msft Virtual Disk] Size: 5.00 GB Alloc: 1.00 GB Free Space: 4.00 GB
VERBOSE: Disk Name: [Msft Virtual Disk] Size: 5.00 GB Alloc: 1.00 GB Free Space: 4.00 GB
VERBOSE: Disk Name: [Msft Virtual Disk] Size: 5.00 GB Alloc: 1.00 GB Free Space: 4.00 GB
VERBOSE: Disk Name: [Msft Virtual Disk] Size: 5.00 GB Alloc: 1.00 GB Free Space: 4.00 GB
VERBOSE: Disk Name: [Msft Virtual Disk] Size: 5.00 GB Alloc: 1.00 GB Free Space: 4.00 GB
VERBOSE: Disk Name: [Msft Virtual Disk] Size: 5.00 GB Alloc: 1.00 GB Free Space: 4.00 GB
VERBOSE: Disk Name: [Msft Virtual Disk] Size: 5.00 GB Alloc: 1.00 GB Free Space: 4.00 GB
VERBOSE: Disk Name: [Msft Virtual Disk] Size: 5.00 GB Alloc: 1.00 GB Free Space: 4.00 GB
VERBOSE: Disk Name: [Msft Virtual Disk] Size: 5.00 GB Alloc: 1.00 GB Free Space: 4.00 GB
VERBOSE: Disk Name: [Msft Virtual Disk] Size: 5.00 GB Alloc: 1.00 GB Free Space: 4.00 GB
VERBOSE: Disk Name: [Msft Virtual Disk] Size: 5.00 GB Alloc: 1.00 GB Free Space: 4.00 GB
VERBOSE: Disk Name: [Msft Virtual Disk] Size: 5.00 GB Alloc: 1.00 GB Free Space: 4.00 GB
VERBOSE: Disk Name: [Msft Virtual Disk] Size: 5.00 GB Alloc: 1.00 GB Free Space: 4.00 GB
VERBOSE: Disk Name: [Msft Virtual Disk] Size: 5.00 GB Alloc: 1.00 GB Free Space: 4.00 GB
VERBOSE: Disk Name: [Msft Virtual Disk] Size: 5.00 GB Alloc: 1.00 GB Free Space: 4.00 GB
VERBOSE: Disk Name: [Msft Virtual Disk] Size: 5.00 GB Alloc: 1.00 GB Free Space: 4.00 GB
VERBOSE: Disk Name: [Msft Virtual Disk] Size: 5.00 GB Alloc: 1.00 GB Free Space: 4.00 GB
VERBOSE: Disk Name: [Msft Virtual Disk] Size: 5.00 GB Alloc: 1.00 GB Free Space: 4.00 GB
VERBOSE: Disk Name: [Msft Virtual Disk] Size: 5.00 GB Alloc: 1.00 GB Free Space: 4.00 GB
VERBOSE: Disk Name: [Msft Virtual Disk] Size: 5.00 GB Alloc: 1.00 GB Free Space: 4.00 GB
VERBOSE: Disk Name: [Msft Virtual Disk] Size: 5.00 GB Alloc: 1.00 GB Free Space: 4.00 GB
VERBOSE: Disk Name: [Msft Virtual Disk] Size: 5.00 GB Alloc: 1.00 GB Free Space: 4.00 GB
VERBOSE: Disk Name: [Msft Virtual Disk] Size: 5.00 GB Alloc: 1.00 GB Free Space: 4.00 GB
VERBOSE:
VERBOSE: -----------------------------------------------------
VERBOSE: Pool: S2DPool
VERBOSE:
VERBOSE: Total Space: 120.00 GB
VERBOSE: Total Alloc: 24.00 GB
VERBOSE: Free Space: 96.00 GB
VERBOSE: Percent Used: 20.00% GB

Automation

Since the CmdLet returns an object, the return values can be used for automation and DevOps scripts.

For example, the following script:

$Usage = Get-ClusterS2DStorageUsage -StoragePool S2DPool
if ($Usage.ReserveRequired -gt $Usage.TotalFreeSpace) { Write-Host "Call Home for Help!" }

Checks the S2DPool Storage Pool has enough excess capacity to rebuild.

This can be used in operations monitoring systems such as PowerShell Desired State Configuration (DSC) environments or potentially any monitoring environment.

Or potentially each disk drive media type can be tested as each disk type is likely to be different capacities.

$Usage = Get-ClusterS2DStorageUsage -StoragePool S2DPool -MediaType SSD
if ($Usage.ReserveRequired -gt $Usage.TotalFreeSpace)
   { Write-Host "Warning, SSD capacity below requirement to rebuild!" }
If ($Usage.PercentUsed -gt “90”)
   { Write-Host "Warning, low on SSD space!" }
Naturally you will replace the Write-Host command with something more appropriate and functional.

PowerShell CmdLet

The following code should be copied into a file called Get-ClusterS2DStorageUsage.psm1 and installed like any PowerShell module. The CmdLet can be used for testing.

Download a copy of this PowerShell CmdLet. Right-click this link to save the Get-ClusterS2DStorageUsage script. Then rename the text file to Get-ClusterS2DStorageUsage.psm1. This PowerShell CmdLet will be imported and used in following steps to create the storage server VMs.
function Get-ClusterS2DStorageUsage {
  <# .SYNOPSIS Return the Pool Storage Usage .DESCRIPTION Describe the function in more detail .EXAMPLE Give an example of how to use it .EXAMPLE Give another example of how to use it .PARAMETER poolname The computer name to query. Just one. .PARAMETER computername The name computer name to query #>
  [CmdletBinding()]
  param
  (
      [Parameter(
      ValueFromPipeline=$True,
      ValueFromPipelineByPropertyName=$True,
        HelpMessage='- FriendlyName ')]
      [Alias('StoragePool')]
      [string[]]$FriendlyName,

      [string]$MediaType = 'all'	
  )

    begin 
    {
        $MediaType = $MediaType.ToUpper()

        Write-Verbose "Checking Cluster Nodes exists"
        $ClusterNodes = Get-ClusterNode
        if ($ClusterNodes) {
            $Msg = $ClusterNodes.Count.ToString() + " cluster nodes found"
            Write-Verbose $Msg
        } else {
            $Msg = "No cluster nodes found"
            Write-Error $Msg
            Return
        }

        if (! $FriendlyName) {
            Write-Verbose "No StoragePool name entered - Getting all Pools"
            $SP = Get-StoragePool | ? IsPrimordial -eq $False
            if ($SP) {
                $FriendlyName = $SP.FriendlyName
                $Msg = "Storage Pool found [" + $FriendlyName + "]"
                Write-Verbose $Msg
            } else {
                Write-Error "Error - No Storage Pools available"
            }
        }
        $Obj = @()  
    }

    Process 
    {
        foreach ($Pool in $FriendlyName) 
        {
            $Msg = "Processing " + $Pool
            Write-Verbose $Msg


            Write-Verbose "Checking StoragePool exists"
            $thisPool = Get-StoragePool -FriendlyName $Pool -ErrorAction Stop

            Write-Verbose "Checking for disks with the MediaType"
            if ( $MediaType -eq "all" )
            {
                $CapacityDisks = $thisPool | Get-PhysicalDisk | ? MediaType -ne "Journal"
            } else {
                $CapacityDisks = $thisPool | Get-PhysicalDisk | ? MediaType -eq $MediaType
            }
            if ($CapacityDisks) {
                $Msg = $CapacityDisks.Count.ToString() + " Disks with MediaType " + $MediaType + " found"
                Write-Verbose $Msg
            } else {
                $Msg = "No qualifying disks found with MediaType:" + $MediaType
                Write-Error $Msg
                Return
            }

            $PoolDiskCount = $CapacityDisks.Count
            $Msg = ("Number of Pool Disks: " + $PoolDiskCount)
            Write-Verbose $Msg
            $TotalSize = 0  
            $TotalAlloc = 0
 
            ForEach ($Disk in $CapacityDisks)
            { 

                $DiskSize = $Disk.Size /1GB
                $DiskAlloc = $Disk.AllocatedSize /1GB
                $FreeSpace = $DiskSize - $DiskAlloc
                $TotalSize += $DiskSize 
                $TotalAlloc += $DiskAlloc 

                $Msg =  "Disk Name: [" + $Disk.FriendlyName + "] ",
                    ("Size: {0:n2} GB  " -f ($DiskSize) ),
                    ("Alloc: {0:n2} GB  " -f ($DiskAlloc)),
                    ("Free Space: {0:n2} GB" -f ($FreeSpace)) 
                Write-Verbose $Msg 
           }


            $PoolFree = $TotalSize - $TotalAlloc
            $PercentFull = $TotalAlloc / $TotalSize * 100

            Write-Verbose " "
            Write-Verbose "-----------------------------------------------------"
            $Msg  = "Pool: " + $thisPool.FriendlyName
            Write-Verbose $Msg
            Write-Verbose " "
            $Msg =  ("Total Space: {0:n2} GB" -f ($TotalSize))
            Write-Verbose $Msg
            $Msg =  ("Total Alloc: {0:n2} GB" -f ($TotalAlloc))
            Write-Verbose $Msg
            $Msg =  ("Free Space: {0:n2} GB" -f ($PoolFree))
            Write-Verbose $Msg
            $Msg =  ("Percent Used: {0:n2}% GB" -f ($PercentFull))
            Write-Verbose $Msg

            $ReserveRequired = $TotalSize / $PoolDiskCount

            $Properties = [Ordered]@{
                'Pool'=$thisPool.FriendlyName;
                'TotalCapacity'=$TotalSize;
                'TotalUsedSpace'=$TotalAlloc;
                'TotalFreeSpace'=$PoolFree;
                'PercentUsed'=$PercentFull
                'UnitOfMeasure'="GB";
                'DiskCount'=$PoolDiskCount;
                'SelectedMediaType'=$MediaType;
                'ReserveRequired'=$ReserveRequired;     
            }
            $Obj += New-Object –Typename PSObject –Property $Properties
        }
        
        Write-Output $Obj
    }
}

Please do provide feedback on errors or any other comments below.

 

Article Attachments

Was this article helpful?

Submit a Comment

Your email address will not be published. Required fields are marked *