Public/cloud-image.ps1

function Get-CloudImage {

    <#
    .SYNOPSIS
        Gets images from the Cloud Server.
     
    .DESCRIPTION
        Retrieves a list of images. Automatically handles token refresh.
     
    .PARAMETER Name
        Optional. Filter by Image name.
     
    .EXAMPLE
        # Get all cloud images
        Get-CloudImage
         
    .EXAMPLE
        # Get the cloud image with the name "web-server-01"
        Get-CloudImage -Name "web-server-01"
 
    .EXAMPLE
        # Get the cloud image with the ID 561
        Get-CloudImage -ID 561
    #>

    
    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $false)]
        [string]$Name,
        
        [Parameter(Mandatory = $false)]
        [int]$ID
    )
    
    # Build the URI - adjust this to match your actual API endpoint
    $uri = "$($script:CloudConnection.BaseUri)/manifold-api/v2/cloud/image"
    
    # Use the helper function which handles token refresh automatically
    $response = Invoke-CloudApiRequest -Uri $uri -Method Get
    
    # Extract images from the response
    $images = $response.images
    
    # Filter by name if specified
    if ($Name) {
        $images = $images | Where-Object { $_.name -like "*$Name*" }
    }
    
    # Filter by ID if specified - use PSBoundParameters to check if parameter was provided
    elseif ($PSBoundParameters.ContainsKey('ID')) {
            $images = $images | Where-Object { ($_.id -eq $ID) -and ($null -ne $_.id) }
    }
    
    return $images
}

function Rename-CloudImage {
    <#
    .SYNOPSIS
        Rename an image in the Cloud Server.
     
    .DESCRIPTION
        Renames an image. Automatically handles token refresh.
     
    .PARAMETER Name
        New name of the image
         
    .PARAMETER ID
        ID of the image
             
    .EXAMPLE
        # Rename cloud image with the ID 5 to "new-name"
        Rename-CloudImage -Name "new-name" -ID 5
    #>

    
    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true)]
        [string]$Name,
        
        [Parameter(Mandatory = $true)]
        [int]$ID
    )
        
    $newname = @{
        name = $Name
    }
    
    $uri = "$($script:CloudConnection.BaseUri)/manifold-api/v2/cloud/image/${ID}/name"
       
    $response = Invoke-CloudApiRequest -Uri $uri -Method Patch -Body $newname
    
    return $response
}

function Lock-CloudImage {
    <#
    .SYNOPSIS
        Lock an image in the Cloud Server.
     
    .DESCRIPTION
        Lock an image. Automatically handles token refresh.
        
    .PARAMETER ID
        ID of the image
 
    .PARAMETER Level
        Lock level (USE, MANAGE, ADMIN, ALL)
 
    .PARAMETER Test
        When set, performs a test lock instead of a real one.
 
    .EXAMPLE
        # Lock the image 5 to the level ADMIN
        Lock-CloudImage -ID 5 -Level ADMIN
 
    .EXAMPLE
        # Shows the results of a test application of lock level USE on image 5
        Lock-CloudImage -ID 5 -Level USE -Test
 
    #>

    
    [CmdletBinding()]
    param(
        
        [Parameter(Mandatory = $true)]
        [int]$ID,
        
        [Parameter(Mandatory = $true)]
        [ValidateSet('USE','MANAGE','ADMIN','ALL')]
        [string]$Level,
        
        [switch]$Test
    )

    $lockdata = @{
        level = $Level
        test  = [bool]$Test
    }
    
    $uri = "$($script:CloudConnection.BaseUri)/manifold-api/v2/cloud/image/${ID}/lock"
    $response = Invoke-CloudApiRequest -Uri $uri -Method Patch -Body $lockdata
    return $response
}

function Unlock-CloudImage {
    <#
    .SYNOPSIS
        Unlock an image in the Cloud Server.
         
    .DESCRIPTION
        Unlock an image. Automatically handles token refresh.
        
    .PARAMETER ID
        ID of the image
 
    .EXAMPLE
        # Unlock image 5
        Unlock-CloudImage -ID 5
    #>

    
    [CmdletBinding()]
    param(
        
        [Parameter(Mandatory = $true)]
        [int]$ID        

    )
   
    $uri = "$($script:CloudConnection.BaseUri)/manifold-api/v2/cloud/image/${ID}/unlock"
    $response = Invoke-CloudApiRequest -Uri $uri -Method Patch
    return $response
}

function Remove-CloudImage {
    <#
    .SYNOPSIS
        Removes an image from the Cloud Server.
     
    .DESCRIPTION
        Removes an image. Automatically handles token refresh.
     
    .PARAMETER ID
        Required. Image ID.
     
    .EXAMPLE
        # Remove image with confirmation prompt (default behavior):
        Remove-CloudImage -ID 3
        Confirm
        Are you sure you want to perform this action?
        Performing the operation "Remove Image" on target "Image ID 3 (MyImage)".
        [Y] Yes [A] Yes to All [N] No [L] No to All [S] Suspend [?] Help (default is "Y"): Y
 
    .EXAMPLE
        # Remove image without confirmation prompt:
        Remove-CloudImage -ID 3 -Confirm:$false
    #>

    
    [CmdletBinding(SupportsShouldProcess, ConfirmImpact='High')]
    param(
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
        [int]$ID
    )
    
    process {
        $uri = "$($script:CloudConnection.BaseUri)/manifold-api/v2/cloud/image/${ID}"
        
        Invoke-CloudResourceRemoval `
            -CallerPSCmdlet $PSCmdlet `
            -ResourceType "Image" `
            -ID $ID `
            -Uri $uri `
            -GetResourceScript { Get-CloudImage -ID $ID }
    }
}

function New-CloudBlankDisk {
    <#
    .SYNOPSIS
        Creates a new generic datablock image on the Cloud Server.
     
    .DESCRIPTION
        Creates a new blank disk image. Automatically handles token refresh.
     
    .PARAMETER Name
        Required. Name of the new image.
     
    .PARAMETER DatastoreID
        Required. Datastore ID (default: 100).
     
    .PARAMETER Size
        Optional. Size in MB (default: 1024).
     
    .EXAMPLE
        # Creates a new blank disk image named "BlankDisk" of default size 1024MB on default datastore 100
        New-CloudBlankDisk -Name "BlankDisk"
         
    .EXAMPLE
        # Creates a 10GB/10240MB disk image named "Data-Disk-01" on datastore 2
        New-CloudBlankDisk -Name "Data-Disk-01" -Size 10240 -DatastoreID 2
    #>

    
    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true)]
        [string]$Name,
        
        [Parameter(Mandatory = $True)]
        [int]$DatastoreID = 100,
        
        [Parameter(Mandatory = $True)]
        [int]$Size = 1024
    )
    
    # Build the configuration - NAME goes INSIDE the template string
    $config = [PSCustomObject]@{
        datastore = $DatastoreID
        template = "NAME=$Name DEV_PREFIX=vd DRIVER=raw SIZE=$Size TYPE=DATABLOCK"
    }
    
    # Build the URI
    $uri = "$($script:CloudConnection.BaseUri)/manifold-api/v2/cloud/image"
    
    Write-Verbose "Creating image '$Name' with size ${Size}MB on datastore $DatastoreID"
    Write-Verbose "Request body: $($config | ConvertTo-Json -Compress)"
    
    # Use the helper function which handles token refresh automatically
    $response = Invoke-CloudApiRequest -Uri $uri -Method Post -Body $config
    
    Write-Host "Image '$Name' created successfully." -ForegroundColor Green
    return $response
}

function New-CloudImage {
    <#
    .SYNOPSIS
        Creates a new image on the Cloud Server.
     
    .DESCRIPTION
        Creates a new image of various types (OS disk, ISO, or data block).
        Can create blank images or upload from a file path.
     
    .PARAMETER Name
        Required. Name of the new image.
     
    .PARAMETER Type
        Required. Type of image to create: OS, CDROM, or Datablock.
     
    .PARAMETER Path
        Optional. Path to source file for uploading (for OS disks or ISOs).
     
    .PARAMETER DatastoreID
        Optional. Datastore ID (default: 100).
     
    .PARAMETER Size
        Optional. Size in MB. Required for blank Datablock images.
     
    .PARAMETER Description
        Optional. Description for the image.
     
    .PARAMETER Driver
        Optional. Disk driver (default: raw). Options: raw, qcow2.
     
    .PARAMETER DevPrefix
        Optional. Device prefix. Auto-determined based on type if not specified.
        - OS: sd (default)
        - CDROM: hd (default)
        - Datablock: vd (default)
     
    .PARAMETER Persistent
        Optional. Make the image persistent (not cloned, directly used).
     
    .EXAMPLE
        # Create a blank data disk
        New-CloudImage -Name "Data-Disk-01" -Type Datablock -Size 10240
         
    .EXAMPLE
        # Upload an ISO
        New-CloudImage -Name "Ubuntu 24.04" -Type CDROM -Path "/path/to/ubuntu.iso"
         
    .EXAMPLE
        # Upload an OS disk
        New-CloudImage -Name "Windows Server 2022" -Type OS -Path "/path/to/disk.qcow2" -Driver qcow2 -Description "Windows Server base image"
         
    .EXAMPLE
        # Create a persistent image
        New-CloudImage -Name "Shared-Data" -Type Datablock -Size 51200 -Persistent
         
    .EXAMPLE
        # Upload a QCOW2 from a URL
        New-CloudImage -Name "Ubuntu-2204-Cloud" -Type OS -Path "https://cloud-images.ubuntu.com/jammy/current/jammy-server-cloudimg-amd64.img" -Driver qcow2 -Description "Ubuntu 22.04 Cloud"
    #>

    
    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true)]
        [string]$Name,
        
        [Parameter(Mandatory = $true)]
        [ValidateSet('OS', 'CDROM', 'Datablock')]
        [string]$Type,
        
        [Parameter(Mandatory = $false)]
        [string]$Path,
        
        [Parameter(Mandatory = $false)]
        [int]$DatastoreID = 100,
        
        [Parameter(Mandatory = $false)]
        [int]$Size,
        
        [Parameter(Mandatory = $false)]
        [string]$Description,
        
        [Parameter(Mandatory = $false)]
        [ValidateSet('raw', 'qcow2', 'vmdk')]
        [string]$Driver = 'raw',
        
        [Parameter(Mandatory = $false)]
        [string]$DevPrefix,
        
        [Parameter(Mandatory = $false)]
        [switch]$Persistent
    )
    
    # Validate Size is provided for blank Datablock images
    if ($Type -eq 'Datablock' -and -not $Path -and -not $Size) {
        throw "Size parameter is required when creating a blank Datablock image."
    }
    
    # Auto-determine DevPrefix based on Type if not specified
    if (-not $DevPrefix) {
        switch ($Type) {
            'OS'        { $DevPrefix = 'sd' }
            'CDROM'     { $DevPrefix = 'hd' }
            'Datablock' { $DevPrefix = 'vd' }
        }
    }
    
    # Build the template string
    $templateParts = @()
    
    # NAME is always first
    $templateParts += "NAME=$Name"
    
    # Add TYPE
    $templateParts += "TYPE=$Type"
    
    # Add DEV_PREFIX
    $templateParts += "DEV_PREFIX=$DevPrefix"
    
    # Add DRIVER
    $templateParts += "DRIVER=$Driver"
    
    # Add PATH if provided (for uploading)
    if ($Path) {
        $templateParts += "PATH=$Path"
    }
    
    # Add SIZE if provided
    if ($Size) {
        $templateParts += "SIZE=$Size"
    }
    
    # Add DESCRIPTION if provided
    if ($Description) {
        $templateParts += "DESCRIPTION=`"$Description`""
    }
    
    # Add PERSISTENT if specified
    if ($Persistent) {
        $templateParts += "PERSISTENT=YES"
    }
    
    # Join all parts with spaces
    $templateString = $templateParts -join ' '
    
    # Build the configuration
    $config = [PSCustomObject]@{
        datastore = $DatastoreID
        template = $templateString
    }
    
    # Build the URI
    $uri = "$($script:CloudConnection.BaseUri)/manifold-api/v2/cloud/image"
    
    Write-Verbose "Creating $Type image '$Name'"
    if ($Path) {
        Write-Verbose "Source path: $Path"
    } elseif ($Size) {
        Write-Verbose "Size: ${Size}MB"
    }
    Write-Verbose "Request body: $($config | ConvertTo-Json -Compress)"
    
    try {
        # Use the helper function which handles token refresh automatically
        $response = Invoke-CloudApiRequest -Uri $uri -Method Post -Body $config
        
        Write-Host "$Type image '$Name' created successfully (ID: $($response.image))." -ForegroundColor Green
        return $response
    }
    catch {
        Write-Error "Failed to create image: $_"
        throw
    }
}

function Update-CloudImageOwner {
    <#
    .SYNOPSIS
        Updates the ownership of an image in the Cloud Server.
     
    .DESCRIPTION
        Changes the user and/or group ownership of an image. Prompts for confirmation.
     
    .PARAMETER ID
        Required. ID of the image.
     
    .PARAMETER UserID
        Required. ID of the user to own the image.
         
    .PARAMETER GroupID
        Optional. ID of the group to own the image.
         
    .EXAMPLE
        # Change the ownership of image 5 to user 2
        Update-CloudImageOwner -ID 5 -UserID 2
         
    .EXAMPLE
        # Change the ownership of image 5 to user 2 and group 100
        Update-CloudImageOwner -ID 5 -UserID 2 -GroupID 100
         
    .EXAMPLE
        # Change the ownership of image 5 to user 2 without confirmation
        Update-CloudImageOwner -ID 5 -UserID 2 -Confirm:$false
    #>

    
    [CmdletBinding(SupportsShouldProcess, ConfirmImpact='High')]
    param(
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
        [int]$ID,
        
        [Parameter(Mandatory = $true)]
        [Alias('User')]
        [int]$UserID,
                
        [Parameter(Mandatory = $false)]
        [Alias('Group')]
        [int]$GroupID
    )
    
    process {
        # Build the update body
        $body = [PSCustomObject]@{
            user = $UserID
        }
        
        if ($PSBoundParameters.ContainsKey('GroupID')) {
            $body | Add-Member -NotePropertyName 'group' -NotePropertyValue $GroupID
        }
        
        $uri = "$($script:CloudConnection.BaseUri)/manifold-api/v2/cloud/image/${ID}/ownership"
        
        # Build action description for confirmation
        $actionParts = @("Change owner to User ID $UserID")
        if ($PSBoundParameters.ContainsKey('GroupID')) {
            $actionParts += "Group ID $GroupID"
        }
        $actionDescription = $actionParts -join " and "
        
        # Use the helper function
        Invoke-CloudResourceUpdate `
            -CallerPSCmdlet $PSCmdlet `
            -ResourceType "Image" `
            -ID $ID `
            -Uri $uri `
            -Body $body `
            -Action $actionDescription `
            -GetResourceScript { Get-CloudImage -ID $ID } `
            -SuccessMessage "Image $ID ownership updated successfully."
    }
}

function Update-CloudImagePermissions {
    <#
    .SYNOPSIS
        Updates the permissions of an image in the Cloud Server.
     
    .DESCRIPTION
        Changes the permission of an image. Prompts for confirmation.
     
    .PARAMETER ID
        Required. ID of the image.
     
    .PARAMETER OwnerUse
        Optional. True or false, enable OwnerUse
 
    .PARAMETER OwnerManage
        Optional. True or false, enable OwnerManage
 
    .PARAMETER OwnerAdmin
        Optional. True or false, enable OwnerAdmin
 
    .PARAMETER GroupUse
        Optional. True or false, enable GroupUse
         
    .PARAMETER GroupManage
        Optional. True or false, enable GroupManage
         
    .PARAMETER GroupAdmin
        Optional. True or false, enable GroupAdmin
         
    .PARAMETER OtherUse
        Optional. True or false, enable OtherUse
         
    .PARAMETER OtherManage
        Optional. True or false, enable OtherManage
         
    .PARAMETER OtherAdmin
        Optional. True or false, enable OtherManage
         
    .EXAMPLE
        # Give group users permission to use the image
        Update-CloudImagePermissions -ID 35 -GroupUse $true
         
    .EXAMPLE
        # Deny group admin permission to the image
        Update-CloudImagePermissions -ID 35 -GroupAdmin $false
         
    .EXAMPLE
        # Set multiple permissions at once
        Update-CloudImagePermissions -ID 35 -GroupUse $true -GroupManage $true -OtherUse $true
    #>


    [CmdletBinding(SupportsShouldProcess, ConfirmImpact='High')]
    param(
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
        [int]$ID,
        
        [Parameter(Mandatory = $false)]
        [bool]$OwnerUse,
        
        [Parameter(Mandatory = $false)]
        [bool]$OwnerManage,
        
        [Parameter(Mandatory = $false)]
        [bool]$OwnerAdmin,
        
        [Parameter(Mandatory = $false)]
        [bool]$GroupUse,
        
        [Parameter(Mandatory = $false)]
        [bool]$GroupManage,
        
        [Parameter(Mandatory = $false)]
        [bool]$GroupAdmin,
        
        [Parameter(Mandatory = $false)]
        [bool]$OtherUse,
        
        [Parameter(Mandatory = $false)]
        [bool]$OtherManage,
        
        [Parameter(Mandatory = $false)]
        [bool]$OtherAdmin
    )
    
    process {
        # Build the permissions object structure as shown in the API spec
        $permissions = @{}
        
        if ($PSBoundParameters.ContainsKey('OwnerUse')) { $permissions['owner_use'] = $OwnerUse }
        if ($PSBoundParameters.ContainsKey('OwnerManage')) { $permissions['owner_manage'] = $OwnerManage }
        if ($PSBoundParameters.ContainsKey('OwnerAdmin')) { $permissions['owner_admin'] = $OwnerAdmin }
        
        if ($PSBoundParameters.ContainsKey('GroupUse')) { $permissions['group_use'] = $GroupUse }
        if ($PSBoundParameters.ContainsKey('GroupManage')) { $permissions['group_manage'] = $GroupManage }
        if ($PSBoundParameters.ContainsKey('GroupAdmin')) { $permissions['group_admin'] = $GroupAdmin }
        
        if ($PSBoundParameters.ContainsKey('OtherUse')) { $permissions['other_use'] = $OtherUse }
        if ($PSBoundParameters.ContainsKey('OtherManage')) { $permissions['other_manage'] = $OtherManage }
        if ($PSBoundParameters.ContainsKey('OtherAdmin')) { $permissions['other_admin'] = $OtherAdmin }
        
        # Create the body with the permissions nested structure
        $body = @{
            permissions = $permissions
        }
        
        $uri = "$($script:CloudConnection.BaseUri)/manifold-api/v2/cloud/image/${ID}/permissions"
        
        Invoke-CloudResourceUpdate `
            -CallerPSCmdlet $PSCmdlet `
            -ResourceType "Image" `
            -ID $ID `
            -Uri $uri `
            -Body $body `
            -Action "Update permissions" `
            -GetResourceScript { Get-CloudImage -ID $ID }
    }
}

function Disable-CloudImage {
    <#
    .SYNOPSIS
        Disables an image from the Cloud Server.
     
    .DESCRIPTION
        Disables an image. Automatically handles token refresh.
     
    .PARAMETER Name
        Required. Image ID.
     
    .EXAMPLE
        # Disable image with confirmation prompt (default behavior):
        Disable-CloudImage -ID 3
        Confirm
        Are you sure you want to perform this action?
        Performing the operation "Disable Image" on target "Image ID 3 (MyImage)".
        [Y] Yes [A] Yes to All [N] No [L] No to All [S] Suspend [?] Help (default is "Y"): Y
 
    .EXAMPLE
        # Disable image without confirmation prompt:
        Disable-CloudImage -ID 3 -Confirm:$false
    #>

    
    [CmdletBinding(SupportsShouldProcess, ConfirmImpact='High')]
    param(
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
        [int]$ID
    )
    
    process {
        $uri = "$($script:CloudConnection.BaseUri)/manifold-api/v2/cloud/image/${ID}/enable"
    $body = @{
      enable = $false
    }
          $actionParts = @("Disable image $ImageID")
        Invoke-CloudResourceUpdate `
            -CallerPSCmdlet $PSCmdlet `
            -ResourceType "Image" `
            -ID $ID `
            -Uri $uri `
            -Body $body `
            -Action "Disable" `
            -GetResourceScript { Get-CloudImage -ID $ID } `
            -SuccessMessage "Image $ID disabled successfully."
    }
}

function Enable-CloudImage {
    <#
    .SYNOPSIS
        Enables an image from the Cloud Server.
     
    .DESCRIPTION
        Enables an image. Automatically handles token refresh.
     
    .PARAMETER Name
        Required. Image ID.
     
    .EXAMPLE
        # Enable image with confirmation prompt (default behavior):
        Enable-CloudImage -ID 3
        Confirm
        Are you sure you want to perform this action?
        Performing the operation "Enable Image" on target "Image ID 3 (MyImage)".
        [Y] Yes [A] Yes to All [N] No [L] No to All [S] Suspend [?] Help (default is "Y"): Y
 
    .EXAMPLE
        # Enable image without confirmation prompt:
        Enable-CloudImage -ID 3 -Confirm:$false
    #>

    
    [CmdletBinding(SupportsShouldProcess, ConfirmImpact='High')]
    param(
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
        [int]$ID
    )
    
    process {
        $uri = "$($script:CloudConnection.BaseUri)/manifold-api/v2/cloud/image/${ID}/enable"
    $body = @{
      enable = $true
    }
        $Action = "update"
        Invoke-CloudResourceUpdate `
            -CallerPSCmdlet $PSCmdlet `
            -ResourceType "Image" `
            -ID $ID `
            -Uri $uri `
            -Body $body `
            -Action "Ensable" `
            -GetResourceScript { Get-CloudImage -ID $ID } `
            -SuccessMessage "Image $ID enabled successfully."
    }
}

function Disable-CloudImagePersistence {
    <#
    .SYNOPSIS
        Sets an image to non-persistent on the Cloud Server.
     
    .DESCRIPTION
         Sets an image to persistent. Automatically handles token refresh.
     
    .PARAMETER Name
        Required. Image ID.
     
    .EXAMPLE
        # Disable image persistence with confirmation prompt (default behavior):
        Disable-CloudImagePersistence -ID 3
        Confirm
        Are you sure you want to perform this action?
        Performing the operation "Disable Persistence" on target "Image ID 3 (MyImage)".
        [Y] Yes [A] Yes to All [N] No [L] No to All [S] Suspend [?] Help (default is "Y"): Y
 
    .EXAMPLE
        # Disable image persistence without confirmation prompt:
        Disable-CloudImagePersistence -ID 3 -Confirm:$false
    #>

    
    [CmdletBinding(SupportsShouldProcess, ConfirmImpact='High')]
    param(
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
        [int]$ID
    )
    
    process {
        $uri = "$($script:CloudConnection.BaseUri)/manifold-api/v2/cloud/image/${ID}/persistent"
    $body = @{
      persistent = $false
    }
        $Action = "update"
        Invoke-CloudResourceUpdate `
            -CallerPSCmdlet $PSCmdlet `
            -ResourceType "Image" `
            -ID $ID `
            -Uri $uri `
            -Body $body `
            -Action "Update" `
            -GetResourceScript { Get-CloudImage -ID $ID } `
            -SuccessMessage "Image $ID persistence disabled successfully."
    }
}

function Enable-CloudImagePersistence {
    <#
    .SYNOPSIS
        Sets an image to persistent on the Cloud Server.
     
    .DESCRIPTION
         Sets an image to persistent. Automatically handles token refresh.
     
    .PARAMETER Name
        Required. Image ID.
     
    .EXAMPLE
        # Enable image persistence with confirmation prompt (default behavior):
        Enable-CloudImagePersistence -ID 3
        Confirm
        Are you sure you want to perform this action?
        Performing the operation "Enable Persistence" on target "Image ID 3 (MyImage)".
        [Y] Yes [A] Yes to All [N] No [L] No to All [S] Suspend [?] Help (default is "Y"): Y
 
    .EXAMPLE
        # Enable image persistence without confirmation prompt:
        Enable-CloudImagePersistence -ID 3 -Confirm:$false
    #>

    
    [CmdletBinding(SupportsShouldProcess, ConfirmImpact='High')]
    param(
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
        [int]$ID
    )
    
    process {
        $uri = "$($script:CloudConnection.BaseUri)/manifold-api/v2/cloud/image/${ID}/persistent"
    $body = @{
      persistent = $true
    }
        $Action = "update"
        Invoke-CloudResourceUpdate `
            -CallerPSCmdlet $PSCmdlet `
            -ResourceType "Image" `
            -ID $ID `
            -Uri $uri `
            -Body $body `
            -Action "Update" `
            -GetResourceScript { Get-CloudImage -ID $ID } `
            -SuccessMessage "Image $ID persistence enabled successfully."
    }
}

function Update-CloudImage {
    <#
    .SYNOPSIS
        Updates an image on the Cloud Server.
     
    .DESCRIPTION
        Updates an image's template. You can choose to merge with the existing template
        or replace it entirely. Merge is enabled by default. Automatically handles token refresh.
         
    .PARAMETER ID
        Required. ID of the image to update.
         
    .PARAMETER Template
        Required. Template content for the image in OpenNebula format.
         
    .PARAMETER Merge
        Optional. If true (default), merges with existing template. If false, replaces the template entirely.
        Use -Merge:$false to completely replace the image configuration.
         
    .EXAMPLE
        # Update image description (merge with existing)
        $template = 'DESCRIPTION="Updated description for this image"'
        Update-CloudImage -ID 123 -Template $template
         
    .EXAMPLE
        # Make an image persistent
        $template = 'PERSISTENT="YES"'
        Update-CloudImage -ID 123 -Template $template
         
    .EXAMPLE
        # Replace entire image template
        $template = @"
        NAME="Ubuntu-2204-Updated"
        DESCRIPTION="Ubuntu 22.04 LTS Server"
        TYPE="OS"
        PERSISTENT="NO"
        "@
        Update-CloudImage -ID 123 -Template $template -Merge:$false
         
    .EXAMPLE
        # Update multiple attributes
        $template = @"
        DESCRIPTION="Production Web Server Image"
        PERSISTENT="YES"
        DEV_PREFIX="sd"
        "@
        Update-CloudImage -ID 456 -Template $template
         
    .EXAMPLE
        # Change image driver
        $template = 'DRIVER="qcow2"'
        Update-CloudImage -ID 789 -Template $template
    #>

    
    [CmdletBinding(SupportsShouldProcess, ConfirmImpact='Medium')]
    param(
        [Parameter(Mandatory = $true)]
        [int]$ID,
        
        [Parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [string]$Template,
        
        [Parameter()]
        [bool]$Merge = $true
    )
    
    process {
        # Build the request body
        $body = @{
            merge = $Merge
            template = $Template
        }
        
        $uri = "$($script:CloudConnection.BaseUri)/manifold-api/v2/cloud/image/${ID}"
        
        if ($PSCmdlet.ShouldProcess("Image ID '$ID'", "Update image")) {
            Write-Verbose "Request URI: $uri"
            Write-Verbose "Merge: $Merge"
            Write-Verbose "Template:`n$Template"
            
            try {
                $response = Invoke-CloudApiRequest -Uri $uri -Method Patch -Body $body
                
                Write-Verbose "Successfully updated image ID: $ID"
                Write-Host "Image ID $ID updated successfully." -ForegroundColor Green
                return $response.image
            }
            catch {
                Write-Error "Failed to update image '$ID': $_"
                throw
            }
        }
    }
}

function Update-CloudImageType {
    <#
    .SYNOPSIS
        Updates an image type on the Cloud Server.
     
    .DESCRIPTION
        Updates an image type.
         
    .PARAMETER ID
        Required. ID of the image to update.
         
    .PARAMETER Type
        Required. use either OS, CDROM, DATABLOCK, or CONTEXT
         
        
    .EXAMPLE
        # Change an image to the OS type
        Update-CloudImageType -ID 123 -Type OS
         
    .EXAMPLE
        # Change an image to the CDROM type
        Update-CloudImageType -ID 123 -Type CDROM
         
    .EXAMPLE
        # Change an image to the DATABLOCK type
        Update-CloudImageType -ID 123 -Type DATABLOCK
         
    .EXAMPLE
        # Change an image to the CONTEXT type
        Update-CloudImageType -ID 123 -Type CONTEXT
 
    #>

    
    [CmdletBinding(SupportsShouldProcess, ConfirmImpact='High')]
    param(
        [Parameter(Mandatory = $true)]
        [int]$ID,
        
        [Parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [string]$Type
    )
    
    process {
        # Build the request body
        $body = @{
            type = $Type
        }
        
        $uri = "$($script:CloudConnection.BaseUri)/manifold-api/v2/cloud/image/${ID}/type"
        
        if ($PSCmdlet.ShouldProcess("Image ID '$ID'", "Update image type")) {
            Write-Verbose "Request URI: $uri"
            Write-Verbose "Template:`n$Template"
            
            try {
                $response = Invoke-CloudApiRequest -Uri $uri -Method Patch -Body $body
                
                Write-Verbose "Successfully updated image $ID to type $Type"
                Write-Host "Image ID $ID updated successfully." -ForegroundColor Green
                return $response.image
            }
            catch {
                Write-Error "Failed to update image type for '$ID': $_"
                throw
            }
        }
    }
}

function Get-CloudImageSnapshot {
    <#
    .SYNOPSIS
        Gets snapshots for Cloud images.
     
    .DESCRIPTION
        Retrieves all snapshots for one or more images. Shows snapshot details including
        date, size, parent relationships, and active status.
     
    .PARAMETER ImageID
        Optional. ID of a specific image to get snapshots for. If not specified, gets
        snapshots for all images.
     
    .PARAMETER SnapshotID
        Optional. Get a specific snapshot by ID.
     
    .EXAMPLE
        # Get all snapshots for image 50
        Get-CloudImageSnapshot -ImageID 50
         
    .EXAMPLE
        # Get a specific snapshot
        Get-CloudImageSnapshot -ImageID 50 -SnapshotID 0
         
    .EXAMPLE
        # Get snapshots for all images
        Get-CloudImageSnapshot
         
    .EXAMPLE
        # Get snapshots from pipeline
        Get-CloudImage -ID 50 | Get-CloudImageSnapshot
         
    .EXAMPLE
        # Get snapshots with formatted date
        Get-CloudImageSnapshot -ImageID 50 | Format-Table image_name, id, name, date_formatted, size
    #>

    
    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $false, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
        [Alias('ID', 'image_id')]
        [int]$ImageID,
        
        [Parameter(Mandatory = $false)]
        [int]$SnapshotID
    )
    
    process {
        try {
            # Get images - either specific one or all
            if ($ImageID) {
                $images = @(Get-CloudImage -ID $ImageID)
            }
            else {
                $images = Get-CloudImage
            }
            
            foreach ($image in $images) {
                Write-Verbose "Getting snapshots for Image ID: $($image.id) ($($image.name))"
                
                # Check if image has snapshots
                if (-not $image.snapshots -or -not $image.snapshots.snapshot) {
                    Write-Verbose "No snapshots found for Image ID: $($image.id)"
                    continue
                }
                
                $snapshots = $image.snapshots.snapshot
                
                # Filter by SnapshotID if specified
                if ($PSBoundParameters.ContainsKey('SnapshotID')) {
                    $snapshots = $snapshots | Where-Object { $_.id -eq $SnapshotID }
                    
                    if (-not $snapshots) {
                        Write-Warning "Snapshot ID $SnapshotID not found for Image ID: $($image.id)"
                        continue
                    }
                }
                
                # Process each snapshot
                foreach ($snapshot in $snapshots) {
                    # Add image context
                    $snapshot | Add-Member -NotePropertyName 'image_id' -NotePropertyValue $image.id -Force
                    $snapshot | Add-Member -NotePropertyName 'image_name' -NotePropertyValue $image.name -Force
                    
                    # Add formatted date
                    if ($snapshot.date -and $snapshot.date -match '^\d+$') {
                        try {
                            $dateFormatted = [DateTimeOffset]::FromUnixTimeSeconds([long]$snapshot.date).LocalDateTime
                            $snapshot | Add-Member -NotePropertyName 'date_formatted' -NotePropertyValue $dateFormatted -Force
                        }
                        catch {
                            $snapshot | Add-Member -NotePropertyName 'date_formatted' -NotePropertyValue $null -Force
                        }
                    }
                    else {
                        $snapshot | Add-Member -NotePropertyName 'date_formatted' -NotePropertyValue $null -Force
                    }
                    
                    # Add snapshot metadata from parent object
                    $snapshot | Add-Member -NotePropertyName 'allow_orphans' -NotePropertyValue $image.snapshots.allow_orphans -Force
                    $snapshot | Add-Member -NotePropertyName 'current_base' -NotePropertyValue $image.snapshots.current_base -Force
                    $snapshot | Add-Member -NotePropertyName 'next_snapshot' -NotePropertyValue $image.snapshots.next_snapshot -Force
                    
                    # Output the enhanced snapshot
                    Write-Output $snapshot
                }
            }
            
        }
        catch {
            Write-Error "Failed to get image snapshots: $_"
            throw
        }
    }
}

function Remove-CloudImageSnapshot {
    <#
    .SYNOPSIS
        Deletes a snapshot from a Cloud image.
     
    .DESCRIPTION
        Removes a specific snapshot from an image. This action cannot be undone.
     
    .PARAMETER ImageID
        Required. ID of the image.
     
    .PARAMETER SnapshotID
        Required. ID of the snapshot to delete.
     
    .EXAMPLE
        # Delete snapshot 0 from image 50
        Remove-CloudImageSnapshot -ImageID 50 -SnapshotID 0
         
    .EXAMPLE
        # Delete without confirmation
        Remove-CloudImageSnapshot -ImageID 50 -SnapshotID 0 -Confirm:$false
    #>

    
    [CmdletBinding(SupportsShouldProcess, ConfirmImpact='High')]
    param(
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
        [Alias('image_id')]
        [int]$ImageID,
        
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
        [Alias('id')]
        [int]$SnapshotID
    )
    
    process {
        # Build the URI
        $uri = "$($script:CloudConnection.BaseUri)/manifold-api/v2/cloud/image/${ImageID}/snapshot/${SnapshotID}"
        
        if ($PSCmdlet.ShouldProcess("Image ID $ImageID", "Delete snapshot ID $SnapshotID")) {
            Write-Verbose "Deleting snapshot ID $SnapshotID from Image ID: $ImageID"
            Write-Verbose "Request URI: $uri"
            
            try {
                $response = Invoke-CloudApiRequest -Uri $uri -Method Delete
                
                Write-Host "Snapshot ID $SnapshotID deleted successfully from Image ID $ImageID." -ForegroundColor Green
                return $response
            }
            catch {
                Write-Error "Failed to delete snapshot from Image ${ImageID}: $_"
                throw
            }
        }
    }
}

function Merge-CloudImageSnapshot {
    <#
    .SYNOPSIS
        Flattens (merges) an image snapshot.
     
    .DESCRIPTION
        Flattens a snapshot into the base image, consolidating the snapshot data.
        This operation merges the snapshot's changes into the parent image.
     
    .PARAMETER ImageID
        Required. ID of the image.
     
    .PARAMETER SnapshotID
        Required. ID of the snapshot to flatten.
     
    .EXAMPLE
        # Flatten snapshot 0 of image 50
        Merge-CloudImageSnapshot -ImageID 50 -SnapshotID 0
         
    .EXAMPLE
        # Flatten without confirmation
        Merge-CloudImageSnapshot -ImageID 50 -SnapshotID 0 -Confirm:$false
    #>

    
    [CmdletBinding(SupportsShouldProcess, ConfirmImpact='High')]
    param(
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
        [Alias('image_id')]
        [int]$ImageID,
        
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
        [Alias('id')]
        [int]$SnapshotID
    )
    
    process {
        # Build the URI
        $uri = "$($script:CloudConnection.BaseUri)/manifold-api/v2/cloud/image/${ImageID}/snapshot/${SnapshotID}/flatten"
        
        if ($PSCmdlet.ShouldProcess("Image ID $ImageID", "Flatten snapshot ID $SnapshotID")) {
            Write-Verbose "Flattening snapshot ID $SnapshotID for Image ID: $ImageID"
            Write-Verbose "Request URI: $uri"
            
            try {
                $response = Invoke-CloudApiRequest -Uri $uri -Method Patch
                
                Write-Host "Snapshot ID $SnapshotID flattened successfully for Image ID $ImageID." -ForegroundColor Green
                return $response
            }
            catch {
                Write-Error "Failed to flatten snapshot for Image ${ImageID}: $_"
                throw
            }
        }
    }
}

function Restore-CloudImageSnapshot {
    <#
    .SYNOPSIS
        Reverts an image to a previous snapshot.
     
    .DESCRIPTION
        Reverts the image to the state captured in the specified snapshot.
        This will discard any changes made after the snapshot was taken.
     
    .PARAMETER ImageID
        Required. ID of the image.
     
    .PARAMETER SnapshotID
        Required. ID of the snapshot to revert to.
     
    .EXAMPLE
        # Revert image 50 to snapshot 0
        Restore-CloudImageSnapshot -ImageID 50 -SnapshotID 0
         
    .EXAMPLE
        # Revert without confirmation
        Restore-CloudImageSnapshot -ImageID 50 -SnapshotID 0 -Confirm:$false
    #>

    
    [CmdletBinding(SupportsShouldProcess, ConfirmImpact='High')]
    param(
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
        [Alias('image_id')]
        [int]$ImageID,
        
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
        [Alias('id')]
        [int]$SnapshotID
    )
    
    process {
        # Build the URI
        $uri = "$($script:CloudConnection.BaseUri)/manifold-api/v2/cloud/image/${ImageID}/snapshot/${SnapshotID}/revert"
        
        if ($PSCmdlet.ShouldProcess("Image ID $ImageID", "Revert to snapshot ID $SnapshotID")) {
            Write-Verbose "Reverting Image ID $ImageID to snapshot ID: $SnapshotID"
            Write-Verbose "Request URI: $uri"
            
            try {
                $response = Invoke-CloudApiRequest -Uri $uri -Method Patch
                
                Write-Host "Image ID $ImageID reverted successfully to snapshot ID $SnapshotID." -ForegroundColor Green
                return $response
            }
            catch {
                Write-Error "Failed to revert snapshot for Image ${ImageID}: $_"
                throw
            }
        }
    }
}