private/Select-OSDeployDiskUSBCLI.ps1

function Select-OSDeployDiskUSBCLI {
    <#
    .SYNOPSIS
        Selects a USB disk in a native console picker
 
    .DESCRIPTION
        Uses supplied disk objects or discovers USB disks with Get-OSDeployDisk, then
        excludes disks outside the configured size limits. Eligible disks are displayed
        in a native console table that supports Up and Down arrow navigation, Enter to
        select, and Escape to cancel.
 
        SelectOne returns a sole eligible disk without displaying the picker. Multiple-disk
        selection requires a nonredirected console. Escape returns Boolean false when Skip
        is specified and otherwise returns no object. This function does not modify disks.
 
    .PARAMETER Input
        Specifies disk objects to filter and select instead of calling Get-OSDeployDisk.
        This parameter accepts pipeline input.
 
    .PARAMETER MinimumSizeGB
        Specifies the exclusive minimum disk size in GiB. Default is 8. Aliases are Min,
        MinGB, and MinSize.
 
    .PARAMETER MaximumSizeGB
        Specifies the exclusive maximum disk size in GiB. Default is 1800. Aliases are Max,
        MaxGB, and MaxSize.
 
    .PARAMETER Skip
        Returns Boolean false when the user presses Escape in the console picker.
 
    .PARAMETER SelectOne
        Returns the eligible disk automatically when exactly one disk is available.
 
    .EXAMPLE
        PS> Select-OSDeployDiskUSBCLI -MinimumSizeGB 16 -MaximumSizeGB 256 -Skip
 
        Displays eligible USB disks in the console picker and returns false if Escape is pressed.
 
    .INPUTS
        System.Object. Accepts disk objects through Input.
 
    .OUTPUTS
        Microsoft.Management.Infrastructure.CimInstance. Returns the selected MSFT_Disk object.
        System.Boolean. Returns false when Skip is specified and the user presses Escape.
 
    .NOTES
        Author: David Segura
        Company: Recast Software
        Version: 1.0.0
        Date: 2026-08-28
 
        Multiple-result selection requires an interactive console.
 
    .LINK
        Get-OSDeployDisk
    #>

    [CmdletBinding()]
    param (
        [Parameter(ValueFromPipeline = $true)]
        [Object]$Input,

        [Alias('Min','MinGB','MinSize')]
        [int]$MinimumSizeGB = 8,

        [Alias('Max','MaxGB','MaxSize')]
        [int]$MaximumSizeGB = 1800,

        [System.Management.Automation.SwitchParameter]$Skip,
        [System.Management.Automation.SwitchParameter]$SelectOne
    )
    #=================================================
    # Get-Disk
    #=================================================
    if ($Input) {
        $Results = $Input
    } else {
        $Results = Get-OSDeployDisk -BusType USB | Where-Object {($_.Size -gt ($MinimumSizeGB * 1GB)) -and ($_.Size -lt ($MaximumSizeGB * 1GB))}
    }
    #=================================================
    # Process Results
    #=================================================
    if ($Results) {
        #=================================================
        # There was only 1 Item, then we will select it automatically
        #=================================================
        if ($PSBoundParameters.ContainsKey('SelectOne')) {
            Write-Verbose "[$($MyInvocation.MyCommand.Name)] Automatically select "
            if (($Results | Measure-Object).Count -eq 1) {
                $SelectedItem = $Results
                Return $SelectedItem
            }
        }
        #=================================================
        # Select an Item
        #=================================================
        $DisplayResults = @($Results | Select-Object -Property Number, BusType, MediaType,`
        @{Name='SizeGB';Expression={[int]($_.Size / 1GB)}},`
        FriendlyName, Model, PartitionStyle,`
        @{Name='Partitions';Expression={$_.NumberOfPartitions}})

        if ([Console]::IsInputRedirected -or [Console]::IsOutputRedirected) {
            Write-Warning "[$(Get-Date -Format s)] An interactive console is required to select a USB disk"
            return
        }

        $columnNames = @('Number', 'BusType', 'MediaType', 'SizeGB', 'FriendlyName', 'Model', 'PartitionStyle', 'Partitions')
        $tableRows = foreach ($row in $DisplayResults) {
            [PSCustomObject]@{
                Disk   = $row
                Values = @($columnNames | ForEach-Object { [string]$row.$_ })
            }
        }

        $columnWidths = foreach ($columnIndex in 0..($columnNames.Count - 1)) {
            $maximumValueLength = ($tableRows | ForEach-Object { $_.Values[$columnIndex].Length } | Measure-Object -Maximum).Maximum
            [Math]::Max($columnNames[$columnIndex].Length, $maximumValueLength)
        }

        $header = (($columnNames | ForEach-Object -Begin { $columnIndex = 0 } -Process {
                    $value = $_.PadRight($columnWidths[$columnIndex])
                    $columnIndex++
                    $value
                }) -join ' ')

        $selectedIndex = 0
        $consoleTop = [Console]::CursorTop
        do {
            [Console]::SetCursorPosition(0, $consoleTop)
            Write-Host
            Write-Host -ForegroundColor DarkGreen 'Select a USB Disk (Up/Down to select, Enter to continue, Escape to cancel)'
            Write-Host -ForegroundColor DarkGray $header

            for ($rowIndex = 0; $rowIndex -lt $tableRows.Count; $rowIndex++) {
                $tableRow = (($tableRows[$rowIndex].Values | ForEach-Object -Begin { $columnIndex = 0 } -Process {
                                $value = $_.PadRight($columnWidths[$columnIndex])
                                $columnIndex++
                                $value
                            }) -join ' ')

                if ($rowIndex -eq $selectedIndex) {
                    Write-Host "> $tableRow" -ForegroundColor Black -BackgroundColor Cyan
                }
                else {
                    Write-Host " $tableRow"
                }
            }

            $key = [Console]::ReadKey($true).Key
            switch ($key) {
                'UpArrow' { if ($selectedIndex -gt 0) { $selectedIndex-- } }
                'DownArrow' { if ($selectedIndex -lt ($tableRows.Count - 1)) { $selectedIndex++ } }
                'Escape' {
                    [Console]::WriteLine()
                    if ($PSBoundParameters.ContainsKey('Skip')) { return $false }
                    return
                }
            }
        } until ($key -eq 'Enter')

        [Console]::WriteLine()
        Return ($Results | Where-Object { $_.Number -eq $tableRows[$selectedIndex].Disk.Number })
        #=================================================
    }
}