Public/VM/Import-VergeDrive.ps1

function Import-VergeDrive {
    <#
    .SYNOPSIS
        Imports a disk image file as a new drive on a VergeOS virtual machine.

    .DESCRIPTION
        Import-VergeDrive creates a new drive on a VM by importing from a disk image
        file (VMDK, QCOW2, VHD, VHDX, OVA, OVF, etc.). The file must already be
        uploaded to the VergeOS media catalog.

        This is the recommended way to import VMs from OVA/OVF/VMDK files:
        1. Create a new VM with New-VergeVM
        2. Import the disk(s) with Import-VergeDrive

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

    .PARAMETER VMName
        The name of the VM to add the imported drive to.

    .PARAMETER VMKey
        The key (ID) of the VM to add the imported drive to.

    .PARAMETER FileKey
        The key (ID) of the disk image file to import.

    .PARAMETER FileName
        The name of the disk image file to import.

    .PARAMETER File
        A file object from Get-VergeFile.

    .PARAMETER Name
        The name for the new drive. If not specified, uses auto-generated name.

    .PARAMETER Interface
        The drive interface type. Default is 'virtio-scsi'.
        Valid values: virtio, ide, ahci, nvme, virtio-scsi, virtio-scsi-dedicated

    .PARAMETER Tier
        The preferred storage tier (1-5).

    .PARAMETER PreserveDriveFormat
        Keep the original drive format instead of converting to raw. Default is false.

    .PARAMETER PassThru
        Return the created drive object.

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

    .EXAMPLE
        Import-VergeDrive -VMName "NewServer" -FileName "debian-12-generic-amd64.qcow2"

        Imports a QCOW2 disk image to the VM.

    .EXAMPLE
        $vm = New-VergeVM -Name "ImportedVM" -CPUCores 2 -RAM 4096 -OSFamily Linux -PassThru
        Import-VergeDrive -VM $vm -FileName "server-disk.vmdk" -Interface virtio-scsi

        Creates a VM and imports a VMDK disk.

    .EXAMPLE
        Get-VergeFile -Type qcow2 -Name "*debian*" | Import-VergeDrive -VMName "DebianServer"

        Imports all matching QCOW2 files as drives.

    .OUTPUTS
        None by default. Verge.Drive when -PassThru is specified.

    .NOTES
        Supported import formats: VMDK, QCOW2, VHD, VHDX, VDI, RAW, OVA, OVF
        The import process converts the disk to VergeOS format unless
        -PreserveDriveFormat is specified.
        Use Get-VergeFile -Type vmdk,qcow2,vhd,vhdx,ova,ovf to see importable files.
    #>

    [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium', DefaultParameterSetName = 'ByVMNameFileName')]
    [OutputType([PSCustomObject])]
    param(
        [Parameter(Mandatory, ValueFromPipeline, ParameterSetName = 'ByVMFileName')]
        [Parameter(Mandatory, ValueFromPipeline, ParameterSetName = 'ByVMFileKey')]
        [Parameter(Mandatory, ValueFromPipeline, ParameterSetName = 'ByVMFile')]
        [PSTypeName('Verge.VM')]
        [PSCustomObject]$VM,

        [Parameter(Mandatory, ParameterSetName = 'ByVMNameFileName')]
        [Parameter(Mandatory, ParameterSetName = 'ByVMNameFileKey')]
        [Parameter(Mandatory, ParameterSetName = 'ByVMNameFile')]
        [string]$VMName,

        [Parameter(Mandatory, ParameterSetName = 'ByVMKeyFileName')]
        [Parameter(Mandatory, ParameterSetName = 'ByVMKeyFileKey')]
        [Parameter(Mandatory, ParameterSetName = 'ByVMKeyFile')]
        [int]$VMKey,

        [Parameter(Mandatory, ParameterSetName = 'ByVMNameFileKey')]
        [Parameter(Mandatory, ParameterSetName = 'ByVMKeyFileKey')]
        [Parameter(Mandatory, ParameterSetName = 'ByVMFileKey')]
        [int]$FileKey,

        [Parameter(Mandatory, Position = 1, ParameterSetName = 'ByVMNameFileName')]
        [Parameter(Mandatory, Position = 1, ParameterSetName = 'ByVMKeyFileName')]
        [Parameter(Mandatory, Position = 1, ParameterSetName = 'ByVMFileName')]
        [string]$FileName,

        [Parameter(Mandatory, ParameterSetName = 'ByVMNameFile')]
        [Parameter(Mandatory, ParameterSetName = 'ByVMKeyFile')]
        [Parameter(Mandatory, ParameterSetName = 'ByVMFile')]
        [PSTypeName('Verge.File')]
        [PSCustomObject]$File,

        [Parameter()]
        [ValidateLength(1, 128)]
        [string]$Name,

        [Parameter()]
        [ValidateSet('virtio', 'ide', 'ahci', 'nvme', 'virtio-scsi', 'virtio-scsi-dedicated')]
        [string]$Interface = 'virtio-scsi',

        [Parameter()]
        [ValidateRange(1, 5)]
        [ValidateScript({
            if ($_ -eq 0) {
                throw "Tier 0 is reserved for system metadata and cannot be used for VM drives."
            }
            $true
        })]
        [int]$Tier,

        [Parameter()]
        [switch]$PreserveDriveFormat,

        [Parameter()]
        [switch]$PassThru,

        [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.'
            )
        }
    }

    process {
        # Resolve VM
        $targetVM = switch -Wildcard ($PSCmdlet.ParameterSetName) {
            'ByVMName*' {
                Get-VergeVM -Name $VMName -Server $Server | Select-Object -First 1
            }
            'ByVMKey*' {
                Get-VergeVM -Key $VMKey -Server $Server
            }
            'ByVM*' {
                $VM
            }
        }

        if (-not $targetVM) {
            Write-Error -Message "VM not found" -ErrorId 'VMNotFound'
            return
        }

        if ($targetVM.IsSnapshot) {
            Write-Error -Message "Cannot import drive to '$($targetVM.Name)': VM is a snapshot" -ErrorId 'CannotModifySnapshot'
            return
        }

        # Resolve file
        $targetFileKey = $null
        $targetFileName = $null

        switch -Wildcard ($PSCmdlet.ParameterSetName) {
            '*FileKey' {
                $targetFileKey = $FileKey
                try {
                    $fileInfo = Get-VergeFile -Key $FileKey -Server $Server
                    $targetFileName = $fileInfo.Name
                }
                catch {
                    $targetFileName = "File $FileKey"
                }
            }
            '*FileName' {
                $fileInfo = Get-VergeFile -Name $FileName -Server $Server | Select-Object -First 1
                if (-not $fileInfo) {
                    Write-Error -Message "File '$FileName' not found in media catalog" -ErrorId 'FileNotFound'
                    return
                }
                $targetFileKey = $fileInfo.Key
                $targetFileName = $fileInfo.Name
            }
            '*File' {
                $targetFileKey = $File.Key
                $targetFileName = $File.Name
            }
        }

        if (-not $targetFileKey) {
            Write-Error -Message "Could not resolve file for import" -ErrorId 'FileNotResolved'
            return
        }

        # Build request body for import drive
        $body = @{
            machine              = $targetVM.MachineKey
            interface            = $Interface
            media                = 'import'
            media_source         = $targetFileKey
            enabled              = $true
            preserve_drive_format = $PreserveDriveFormat.IsPresent
        }

        if ($Name) {
            $body['name'] = $Name
        }

        if ($Tier) {
            $body['preferred_tier'] = $Tier.ToString()
        }

        $driveName = if ($Name) { $Name } else { $targetFileName }

        if ($PSCmdlet.ShouldProcess($targetVM.Name, "Import drive from '$targetFileName'")) {
            try {
                Write-Verbose "Importing '$targetFileName' as drive to VM '$($targetVM.Name)'"
                $response = Invoke-VergeAPI -Method POST -Endpoint 'machine_drives' -Body $body -Connection $Server

                if ($response) {
                    $driveKey = $response.'$key' ?? $response.key
                    Write-Verbose "Drive import initiated with key: $driveKey"

                    if ($PassThru -and $driveKey) {
                        # Wait briefly for drive to be created
                        Start-Sleep -Seconds 2
                        Get-VergeDrive -VM $targetVM -Server $Server | Where-Object { $_.Key -eq $driveKey }
                    }
                }
            }
            catch {
                Write-Error -Message "Failed to import drive to VM '$($targetVM.Name)': $($_.Exception.Message)" -ErrorId 'DriveImportFailed'
            }
        }
    }
}