Public/userdata.ps1

function Remove-CSUserData {
    <#
    .SYNOPSIS
        Deletes registered CloudStack user data.

    .DESCRIPTION
        Wraps the deleteUserData API. Removes a registered user data entry by ID.
        -Account must be paired with -DomainId. Accepts user data objects on the
        pipeline.

    .PARAMETER UserDataId
        The user data to delete. Binds from a piped object's id.

    .PARAMETER Account
        The owning account. Must be used with -DomainId.

    .PARAMETER DomainId
        The owning domain

    .PARAMETER ProjectId
        The owning project

    .EXAMPLE
        Remove-CSUserData -UserDataId 'userdata-uuid' -Confirm:$false
        Deletes a user data entry without prompting.

    .EXAMPLE
        Get-CSUserData -Name 'bootstrap-config' | Remove-CSUserData
        Deletes a user data entry located by name.
    #>

    [CmdletBinding(SupportsShouldProcess=$true, ConfirmImpact='High')]
    param([Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true)][Alias('Id')][string]$UserDataId, [string]$Account, [string]$DomainId, [string]$ProjectId)
    process {
        if ($PSBoundParameters.ContainsKey('Account') -and -not $PSBoundParameters.ContainsKey('DomainId')) { throw 'DomainId is required when Account is specified.' }
        $apiParams = @{ id = $UserDataId }
        $parameterMap = @{ Account = 'account'; DomainId = 'domainid'; ProjectId = 'projectid' }
        foreach ($parameter in $parameterMap.Keys) { if ($PSBoundParameters.ContainsKey($parameter)) { $apiParams[$parameterMap[$parameter]] = (Get-Variable -Name $parameter -ValueOnly) } }
        if ($PSCmdlet.ShouldProcess("user data $UserDataId", 'Delete')) { Invoke-CSApiRequest -Command 'deleteUserData' -Parameters $apiParams }
    }
}

function Set-CSUserDataTemplate {
    <#
    .SYNOPSIS
        Links or unlinks user data from a template or ISO.

    .DESCRIPTION
        Wraps the linkUserDataToTemplate API. Associates a registered user data entry
        with a template or an ISO (specify exactly one of -TemplateId or -IsoId), and
        sets how a deploy-time user data override is handled. Omit -UserDataId to
        unlink.

    .PARAMETER TemplateId
        The template to link. Use this or -IsoId, not both.

    .PARAMETER IsoId
        The ISO to link. Use this or -TemplateId, not both.

    .PARAMETER UserDataId
        The user data to link. Omit to unlink any current user data.

    .PARAMETER UserDataPolicy
        Override policy: ALLOWOVERRIDE, APPEND, or DENYOVERRIDE

    .EXAMPLE
        Set-CSUserDataTemplate -TemplateId 'template-uuid' -UserDataId 'userdata-uuid' -UserDataPolicy APPEND
        Links user data to a template, appending any deploy-time user data.

    .EXAMPLE
        Set-CSUserDataTemplate -IsoId 'iso-uuid'
        Unlinks user data from an ISO (no -UserDataId given).
    #>

    [CmdletBinding()]
    param([string]$TemplateId, [string]$IsoId, [string]$UserDataId, [ValidateSet('ALLOWOVERRIDE', 'APPEND', 'DENYOVERRIDE')][string]$UserDataPolicy)
    if ($PSBoundParameters.ContainsKey('TemplateId') -eq $PSBoundParameters.ContainsKey('IsoId')) { throw 'Specify exactly one of TemplateId or IsoId.' }
    $apiParams = @{}
    $parameterMap = @{ TemplateId = 'templateid'; IsoId = 'isoid'; UserDataId = 'userdataid'; UserDataPolicy = 'userdatapolicy' }
    foreach ($parameter in $parameterMap.Keys) { if ($PSBoundParameters.ContainsKey($parameter)) { $apiParams[$parameterMap[$parameter]] = (Get-Variable -Name $parameter -ValueOnly) } }
    Invoke-CSApiRequest -Command 'linkUserDataToTemplate' -Parameters $apiParams
}

function Get-CSUserData {
    <#
    .SYNOPSIS
        Lists registered CloudStack user data.

    .DESCRIPTION
        Wraps the listUserData API, returning the registered user data entries. Filter
        by ID, name, or owner, and use -ListAll/-IsRecursive for broader admin scope.

    .PARAMETER Id
        Filter by user data ID

    .PARAMETER Name
        Filter by name

    .PARAMETER Account
        Filter by account name. Must be used with -DomainId.

    .PARAMETER DomainId
        Filter by domain ID

    .PARAMETER IsRecursive
        With -DomainId, also include subdomains

    .PARAMETER Keyword
        Filter by keyword

    .PARAMETER ListAll
        List every user data entry the caller is allowed to see

    .PARAMETER Page
        Page number of results to return

    .PARAMETER PageSize
        Number of results per page

    .PARAMETER ProjectId
        Filter by project ID

    .EXAMPLE
        Get-CSUserData -Name 'bootstrap-config' -ListAll
        Finds a user data entry by name across all visible accounts.

    .EXAMPLE
        Get-CSUserData -DomainId $domainId -IsRecursive
        Lists user data in a domain and its subdomains.
    #>

    [CmdletBinding()]
    param([string]$Id, [string]$Name, [string]$Account, [string]$DomainId, [switch]$IsRecursive, [string]$Keyword, [switch]$ListAll, [int]$Page, [int]$PageSize, [string]$ProjectId)
    $apiParams = @{}
    $parameterMap = @{ Id = 'id'; Name = 'name'; Account = 'account'; DomainId = 'domainid'; Keyword = 'keyword'; Page = 'page'; PageSize = 'pagesize'; ProjectId = 'projectid' }
    foreach ($parameter in $parameterMap.Keys) { if ($PSBoundParameters.ContainsKey($parameter)) { $apiParams[$parameterMap[$parameter]] = (Get-Variable -Name $parameter -ValueOnly) } }
    if ($IsRecursive) { $apiParams['isrecursive'] = 'true' }; if ($ListAll) { $apiParams['listall'] = 'true' }
    $response = Invoke-CSApiRequest -Command 'listUserData' -Parameters $apiParams
    if ($response.listuserdataresponse.userdata) { return $response.listuserdataresponse.userdata }
    Write-Verbose 'No user data found matching the criteria.'
}

function Register-CSUserData {
    <#
    .SYNOPSIS
        Registers base64-encoded user data.

    .DESCRIPTION
        Wraps the registerUserData API. Stores a named, base64-encoded user data
        (cloud-init) payload that can later be linked to a template/ISO or passed at
        deploy time. -Params lists the variable names the payload expects. -Account
        must be paired with -DomainId.

    .PARAMETER Name
        A unique name for the user data

    .PARAMETER UserData
        The base64-encoded user data payload

    .PARAMETER Account
        The owning account. Must be used with -DomainId.

    .PARAMETER DomainId
        The owning domain

    .PARAMETER Params
        Names of the variables the payload expects, as an array

    .PARAMETER ProjectId
        The owning project

    .EXAMPLE
        Register-CSUserData -Name 'bootstrap-config' -UserData 'IyEvYmluL2Jhc2gKZWNobyBoZWxsbw==' -Params 'hostname','role'
        Registers a cloud-init payload that takes two variables.

    .EXAMPLE
        $b64 = [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes((Get-Content ./cloud-init.yaml -Raw)))
        Register-CSUserData -Name 'web-init' -UserData $b64
        Encodes a local cloud-init file and registers it.
    #>

    [CmdletBinding()]
    param([Parameter(Mandatory=$true)][string]$Name, [Parameter(Mandatory=$true)][string]$UserData, [string]$Account, [string]$DomainId, [string[]]$Params, [string]$ProjectId)
    if ($PSBoundParameters.ContainsKey('Account') -and -not $PSBoundParameters.ContainsKey('DomainId')) { throw 'DomainId is required when Account is specified.' }
    $apiParams = @{ name = $Name; userdata = $UserData }
    $parameterMap = @{ Account = 'account'; DomainId = 'domainid'; ProjectId = 'projectid' }
    foreach ($parameter in $parameterMap.Keys) { if ($PSBoundParameters.ContainsKey($parameter)) { $apiParams[$parameterMap[$parameter]] = (Get-Variable -Name $parameter -ValueOnly) } }
    if ($PSBoundParameters.ContainsKey('Params')) { $apiParams['params'] = $Params -join ',' }
    Invoke-CSApiRequest -Command 'registerUserData' -Parameters $apiParams
}