Public/VM/Get-VergeDrive.ps1

function Get-VergeDrive {
    <#
    .SYNOPSIS
        Retrieves drives attached to a VergeOS virtual machine.

    .DESCRIPTION
        Get-VergeDrive retrieves drive information for one or more VMs, including
        disk size, interface type, media type, and storage tier details.

    .PARAMETER VM
        A VM object from Get-VergeVM. Accepts pipeline input.

    .PARAMETER VMName
        The name of the VM to get drives for.

    .PARAMETER VMKey
        The key (ID) of the VM to get drives for.

    .PARAMETER Name
        Filter drives by name. Supports wildcards (* and ?).

    .PARAMETER Media
        Filter drives by media type: Disk, CDROM, EFIDisk.

    .PARAMETER Server
        The VergeOS connection to use. Defaults to the current default connection.

    .EXAMPLE
        Get-VergeDrive -VMName "WebServer01"

        Gets all drives attached to the VM named "WebServer01".

    .EXAMPLE
        Get-VergeVM -Name "WebServer01" | Get-VergeDrive

        Gets drives for the VM using pipeline input.

    .EXAMPLE
        Get-VergeVM -Name "Prod-*" | Get-VergeDrive | Where-Object { $_.UsedGB -gt 100 }

        Gets drives larger than 100GB for all production VMs.

    .EXAMPLE
        Get-VergeDrive -VMName "WebServer01" -Media CDROM

        Gets only CD-ROM drives for the specified VM.

    .EXAMPLE
        Get-VergeVM | Get-VergeDrive | Format-Table VMName, Name, Interface, SizeGB, UsedGB, Tier

        Lists all drives across all VMs in a table format.

    .OUTPUTS
        PSCustomObject with PSTypeName 'Verge.Drive'

    .NOTES
        Use New-VergeDrive and Remove-VergeDrive to manage VM drives.
        Use Set-VergeDrive to modify drive settings or change CD/ISO media.
    #>

    [CmdletBinding(DefaultParameterSetName = 'ByVMObject')]
    [OutputType([PSCustomObject])]
    param(
        [Parameter(Mandatory, ValueFromPipeline, ParameterSetName = 'ByVMObject')]
        [PSTypeName('Verge.VM')]
        [PSCustomObject]$VM,

        [Parameter(Mandatory, ParameterSetName = 'ByVMName')]
        [string]$VMName,

        [Parameter(Mandatory, ParameterSetName = 'ByVMKey')]
        [int]$VMKey,

        [Parameter()]
        [SupportsWildcards()]
        [string]$Name,

        [Parameter()]
        [ValidateSet('Disk', 'CDROM', 'EFIDisk')]
        [string]$Media,

        [Parameter()]
        [object]$Server
    )

    begin {
        # Resolve connection
        if (-not $Server) {
            $Server = $script:DefaultConnection
        }
        if (-not $Server) {
            throw [System.InvalidOperationException]::new(
                'Not connected to VergeOS. Use Connect-VergeOS to establish a connection.'
            )
        }

        # Map friendly media names to API values
        $mediaMap = @{
            'Disk'    = 'disk'
            'CDROM'   = 'cdrom'
            'EFIDisk' = 'efidisk'
        }

        # Map API interface values to friendly names
        $interfaceDisplayMap = @{
            'virtio'               = 'Virtio (Legacy)'
            'ide'                  = 'IDE'
            'ahci'                 = 'SATA (AHCI)'
            'nvme'                 = 'NVMe'
            'virtio-scsi'          = 'Virtio-SCSI'
            'virtio-scsi-dedicated' = 'Virtio-SCSI (Dedicated)'
            'lsi53c895a'           = 'LSI SCSI'
            'megasas'              = 'LSI MegaRAID SAS'
            'megasas-gen2'         = 'LSI MegaRAID SAS 2'
            'usb'                  = 'USB'
        }
    }

    process {
        # Resolve VM based on parameter set
        $targetVMs = switch ($PSCmdlet.ParameterSetName) {
            'ByVMName' {
                Get-VergeVM -Name $VMName -Server $Server
            }
            'ByVMKey' {
                Get-VergeVM -Key $VMKey -Server $Server
            }
            'ByVMObject' {
                $VM
            }
        }

        foreach ($targetVM in $targetVMs) {
            if (-not $targetVM -or -not $targetVM.MachineKey) {
                continue
            }

            # Build query for drives
            $queryParams = @{}

            # Filter by machine (VM's internal machine key)
            $filters = [System.Collections.Generic.List[string]]::new()
            $filters.Add("machine eq $($targetVM.MachineKey)")

            # Filter by media type if specified
            if ($Media) {
                $filters.Add("media eq '$($mediaMap[$Media])'")
            }

            $queryParams['filter'] = $filters -join ' and '

            # Request drive fields including related data
            $queryParams['fields'] = @(
                '$key'
                'name'
                'orderid'
                'interface'
                'media'
                'description'
                'enabled'
                'serial'
                'preferred_tier'
                'readonly'
                'disksize'
                'used_bytes'
                'media_source'
                'machine'
                'status#status as status'
                'status#display(status) as status_display'
                'media_source#name as media_file'
                'media_source#allocated_bytes as allocated_bytes'
                'media_source#used_bytes as file_used_bytes'
                'media_source#filesize as filesize'
            ) -join ','

            $queryParams['sort'] = '+orderid'

            try {
                Write-Verbose "Querying drives for VM '$($targetVM.Name)' (Machine: $($targetVM.MachineKey))"
                $response = Invoke-VergeAPI -Method GET -Endpoint 'machine_drives' -Query $queryParams -Connection $Server

                # Handle both single object and array responses
                $drives = if ($response -is [array]) { $response } elseif ($response) { @($response) } else { @() }

                foreach ($drive in $drives) {
                    if (-not $drive -or -not $drive.name) {
                        continue
                    }

                    # Apply name filter if specified (with wildcard support)
                    if ($Name) {
                        if ($Name -match '[\*\?]') {
                            if ($drive.name -notlike $Name) {
                                continue
                            }
                        }
                        elseif ($drive.name -ne $Name) {
                            continue
                        }
                    }

                    # Calculate sizes in GB
                    $sizeBytes = if ($drive.disksize) { $drive.disksize } elseif ($drive.allocated_bytes) { $drive.allocated_bytes } else { 0 }
                    $usedBytes = if ($drive.used_bytes) { $drive.used_bytes } elseif ($drive.file_used_bytes) { $drive.file_used_bytes } else { 0 }

                    # Map media type to friendly name
                    $mediaDisplay = switch ($drive.media) {
                        'cdrom'    { 'CD-ROM' }
                        'disk'     { 'Disk' }
                        'efidisk'  { 'EFI Disk' }
                        'import'   { 'Import Disk' }
                        '9p'       { 'Pass-Through (9P)' }
                        'dir'      { 'Pass-Through (Directory)' }
                        'clone'    { 'Clone Disk' }
                        'nonpersistent' { 'Non-Persistent' }
                        default    { $drive.media }
                    }

                    # Get interface display name
                    $interfaceDisplay = if ($interfaceDisplayMap.ContainsKey($drive.interface)) {
                        $interfaceDisplayMap[$drive.interface]
                    } else {
                        $drive.interface
                    }

                    # Create output object
                    $output = [PSCustomObject]@{
                        PSTypeName       = 'Verge.Drive'
                        Key              = [int]$drive.'$key'
                        Name             = $drive.name
                        OrderId          = [int]$drive.orderid
                        Interface        = $drive.interface
                        InterfaceDisplay = $interfaceDisplay
                        Media            = $drive.media
                        MediaDisplay     = $mediaDisplay
                        Description      = $drive.description
                        Enabled          = [bool]$drive.enabled
                        ReadOnly         = [bool]$drive.readonly
                        Serial           = $drive.serial
                        Tier             = $drive.preferred_tier
                        SizeBytes        = [long]$sizeBytes
                        SizeGB           = [math]::Round($sizeBytes / 1GB, 2)
                        UsedBytes        = [long]$usedBytes
                        UsedGB           = [math]::Round($usedBytes / 1GB, 2)
                        MediaSource      = $drive.media_source
                        MediaFile        = $drive.media_file
                        Status           = $drive.status
                        StatusDisplay    = $drive.status_display
                        VMKey            = $targetVM.Key
                        VMName           = $targetVM.Name
                        MachineKey       = $targetVM.MachineKey
                    }

                    # Add hidden property for pipeline support
                    $output | Add-Member -MemberType NoteProperty -Name '_Connection' -Value $Server -Force
                    $output | Add-Member -MemberType NoteProperty -Name '_VM' -Value $targetVM -Force

                    Write-Output $output
                }
            }
            catch {
                Write-Error -Message "Failed to get drives for VM '$($targetVM.Name)': $($_.Exception.Message)" -ErrorId 'GetDrivesFailed'
            }
        }
    }
}