Modules/businessdev.ALbuild.Containers/Private/Get-BcHostVolumeInfo.ps1

function Get-BcHostVolumeInfo {
    <#
    .SYNOPSIS
        What a path's volume is: file system, drive type, free space, and whether deduplication is on.
 
    .DESCRIPTION
        Docker's Windows storage driver ('windowsfilter') is particular about where it lives, and each
        requirement has a failure mode that only shows up much later:
 
          * NTFS. ReFS is not supported; layers fail to mount.
          * A fixed local disk. A network or removable path cannot host layers at all.
          * No Data Deduplication. Dedup on the Docker root rewrites layer files underneath the driver
            and corrupts images - the damage surfaces as containers that cannot be removed.
 
        Everything is best-effort and reported as $null when it cannot be determined, so a caller can
        distinguish "not NTFS" from "could not tell". Get-Volume and Get-DedupVolume are absent on some
        editions and inside containers, which must not turn into a hard failure.
 
        One function so callers have one thing to mock; the real cmdlets are never reached in tests.
 
    .PARAMETER Path
        The path whose volume is inspected. It need not exist yet - its root does.
 
    .OUTPUTS
        PSCustomObject with Drive, FileSystem, DriveType, FreeGb, TotalGb, DeduplicationEnabled.
    #>

    [CmdletBinding()]
    [OutputType([PSCustomObject])]
    param(
        [Parameter(Mandatory)] [ValidateNotNullOrEmpty()] [string] $Path
    )

    $result = [PSCustomObject]@{
        Drive                = $null
        FileSystem           = $null
        DriveType            = $null
        FreeGb               = $null
        TotalGb              = $null
        DeduplicationEnabled = $null
    }

    $root = $null
    try { $root = [System.IO.Path]::GetPathRoot($Path) } catch { }
    if ([string]::IsNullOrWhiteSpace($root)) { return $result }
    $result.Drive = $root

    # DriveInfo covers type and space on every edition, so it comes first and stands alone.
    try {
        $di = New-Object System.IO.DriveInfo($root)
        if ($di.IsReady) {
            $result.DriveType = "$($di.DriveType)"
            $result.FileSystem = "$($di.DriveFormat)"
            $result.FreeGb = [Math]::Round($di.AvailableFreeSpace / 1GB, 1)
            $result.TotalGb = [Math]::Round($di.TotalSize / 1GB, 1)
        }
        else {
            # Not ready is an answer, not an error: an empty removable drive lands here.
            $result.DriveType = "$($di.DriveType)"
        }
    }
    catch { Write-Verbose "Could not read volume information for '$root': $($_.Exception.Message)" }

    # Deduplication is a server feature; the cmdlet is missing on client editions, which is not an error.
    if (Get-Command -Name 'Get-DedupVolume' -ErrorAction SilentlyContinue) {
        try {
            $letter = "$root".TrimEnd('\', '/')
            $dedup = Get-DedupVolume -Volume $letter -ErrorAction Stop
            $result.DeduplicationEnabled = [bool]$dedup.Enabled
        }
        catch {
            # Get-DedupVolume throws when the volume simply is not enabled for dedup - which is the
            # answer we want, not a failure.
            $result.DeduplicationEnabled = $false
        }
    }

    return $result
}