Public/Tenant/New-VergeSharedObject.ps1

function New-VergeSharedObject {
    <#
    .SYNOPSIS
        Shares a VM with a VergeOS tenant.

    .DESCRIPTION
        New-VergeSharedObject shares a VM from the parent system with a tenant.
        The shared VM can then be imported by the tenant to create their own copy.
        This is useful for distributing templates or pre-configured VMs to tenants.

    .PARAMETER Tenant
        A tenant object from Get-VergeTenant. Accepts pipeline input.

    .PARAMETER TenantName
        The name of the tenant to share the VM with.

    .PARAMETER TenantKey
        The unique key (ID) of the tenant to share the VM with.

    .PARAMETER VM
        A VM object from Get-VergeVM to share.

    .PARAMETER VMName
        The name of the VM to share.

    .PARAMETER VMKey
        The unique key (ID) of the VM to share.

    .PARAMETER Name
        The name for the shared object (how it appears to the tenant).
        Defaults to the VM name if not specified.

    .PARAMETER Description
        Optional description for the shared object.

    .PARAMETER PassThru
        Return the created shared object.

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

    .EXAMPLE
        New-VergeSharedObject -TenantName "Customer01" -VMName "Ubuntu-Template"

        Shares the VM "Ubuntu-Template" with the tenant.

    .EXAMPLE
        Get-VergeVM -Name "Windows-Base" | New-VergeSharedObject -TenantName "Customer01" -Name "Windows Server Template"

        Shares a VM with a custom name for the tenant.

    .EXAMPLE
        New-VergeSharedObject -TenantName "Customer01" -VMName "AppServer" -Description "Pre-configured application server" -PassThru

        Shares a VM with description and returns the shared object.

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

    .NOTES
        The VM must exist and be accessible. A snapshot of the VM will be created
        for sharing. The tenant can then import this shared object to create
        their own copy.
    #>

    [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium', DefaultParameterSetName = 'ByNames')]
    [OutputType([PSCustomObject])]
    param(
        [Parameter(Mandatory, ParameterSetName = 'ByTenantVM')]
        [PSTypeName('Verge.Tenant')]
        [PSCustomObject]$Tenant,

        [Parameter(Mandatory, Position = 0, ParameterSetName = 'ByNames')]
        [Parameter(Mandatory, ParameterSetName = 'ByTenantNameVMKey')]
        [string]$TenantName,

        [Parameter(Mandatory, ParameterSetName = 'ByTenantKeyVMName')]
        [Parameter(Mandatory, ParameterSetName = 'ByTenantKeyVMKey')]
        [int]$TenantKey,

        [Parameter(Mandatory, ValueFromPipeline, ParameterSetName = 'ByTenantVM')]
        [PSTypeName('Verge.VM')]
        [PSCustomObject]$VM,

        [Parameter(Mandatory, Position = 1, ParameterSetName = 'ByNames')]
        [Parameter(Mandatory, ParameterSetName = 'ByTenantKeyVMName')]
        [string]$VMName,

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

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

        [Parameter()]
        [ValidateLength(0, 2048)]
        [string]$Description,

        [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 tenant
        $targetTenant = switch -Wildcard ($PSCmdlet.ParameterSetName) {
            '*TenantName*' {
                Get-VergeTenant -Name $TenantName -Server $Server
            }
            '*TenantKey*' {
                Get-VergeTenant -Key $TenantKey -Server $Server
            }
            'ByTenantVM' {
                $Tenant
            }
            'ByNames' {
                Get-VergeTenant -Name $TenantName -Server $Server
            }
        }

        if (-not $targetTenant) {
            Write-Error -Message "Tenant not found." -ErrorId 'TenantNotFound'
            return
        }

        # Resolve VM
        $targetVM = switch -Wildcard ($PSCmdlet.ParameterSetName) {
            '*VMName*' {
                Get-VergeVM -Name $VMName -Server $Server
            }
            '*VMKey*' {
                Get-VergeVM -Key $VMKey -Server $Server
            }
            'ByTenantVM' {
                $VM
            }
            'ByNames' {
                Get-VergeVM -Name $VMName -Server $Server
            }
        }

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

        foreach ($t in $targetTenant) {
            if (-not $t) {
                continue
            }

            # Check if tenant is a snapshot
            if ($t.IsSnapshot) {
                Write-Error -Message "Cannot share VM with tenant '$($t.Name)': Tenant is a snapshot." -ErrorId 'CannotModifySnapshot'
                continue
            }

            foreach ($v in $targetVM) {
                if (-not $v) {
                    continue
                }

                # Determine the shared object name
                $sharedName = if ($Name) { $Name } else { $v.Name }

                # Build request body
                $body = @{
                    recipient = $t.Key
                    type      = 'vm'
                    name      = $sharedName
                    id        = "vms/$($v.Key)"
                }

                # Add optional description
                if ($Description) {
                    $body['description'] = $Description
                }

                # Confirm action
                if ($PSCmdlet.ShouldProcess("$($t.Name)", "Share VM '$($v.Name)' as '$sharedName'")) {
                    try {
                        Write-Verbose "Sharing VM '$($v.Name)' with tenant '$($t.Name)' as '$sharedName'"
                        $response = Invoke-VergeAPI -Method POST -Endpoint 'shared_objects' -Body $body -Connection $Server

                        Write-Verbose "VM '$($v.Name)' shared with tenant '$($t.Name)'"

                        if ($PassThru) {
                            # Wait briefly then return the new shared object
                            Start-Sleep -Milliseconds 500
                            Get-VergeSharedObject -TenantKey $t.Key -Name $sharedName -Server $Server
                        }
                    }
                    catch {
                        $errorMessage = $_.Exception.Message
                        if ($errorMessage -match 'already exists') {
                            Write-Error -Message "A shared object named '$sharedName' already exists for tenant '$($t.Name)'." -ErrorId 'SharedObjectExists'
                        }
                        else {
                            Write-Error -Message "Failed to share VM with tenant '$($t.Name)': $errorMessage" -ErrorId 'ShareVMFailed'
                        }
                    }
                }
            }
        }
    }
}