Public/certificate.ps1

# Certificates: the CA plugin (issue/provision/revoke client certs for hosts and
# the console proxy) and the direct-download certificates KVM hosts use to fetch
# templates over HTTPS.

function Get-CSCAProvider {
    <#
    .SYNOPSIS
        Lists the available certificate authority providers.

    .DESCRIPTION
        Wraps listCAProviders. These are the CA plugins CloudStack can use to issue
        and revoke client certificates; the provider name is what the other
        certificate commands take as -Provider.

    .PARAMETER Name
        Filter by provider name

    .EXAMPLE
        Get-CSCAProvider
        Lists every configured CA provider.
    #>

    [CmdletBinding()]
    param(
        [string]$Name
    )

    $apiParams = @{}
    if ($PSBoundParameters.ContainsKey('Name')) { $apiParams['name'] = $Name }
    ConvertFrom-CSResponse -Response (Invoke-CSApiRequest -Command 'listCAProviders' -Parameters $apiParams) -Command 'listCAProviders'
}

function Get-CSCACertificate {
    <#
    .SYNOPSIS
        Gets the CA public certificate(s).

    .DESCRIPTION
        Wraps listCaCertificate, returning the CA chain the configured (or
        -Provider) CA plugin signs with, so clients can trust issued certificates.

    .PARAMETER Provider
        The CA provider to read the certificate from

    .EXAMPLE
        Get-CSCACertificate
        Returns the default CA plugin's public certificate chain.

    .EXAMPLE
        Get-CSCACertificate -Provider root
        Returns a specific provider's CA certificate.
    #>

    [CmdletBinding()]
    param(
        [string]$Provider
    )

    $apiParams = @{}
    if ($PSBoundParameters.ContainsKey('Provider')) { $apiParams['provider'] = $Provider }
    ConvertFrom-CSResponse -Response (Invoke-CSApiRequest -Command 'listCaCertificate' -Parameters $apiParams) -Command 'listCaCertificate'
}

function New-CSCertificate {
    <#
    .SYNOPSIS
        Issues a client certificate from the CA plugin.

    .DESCRIPTION
        Wraps issueCertificate. Supply a CSR with -Csr to have the CA sign it, or
        -Domain (and optionally -IpAddress) to have the CA generate the key pair and
        certificate for you. Returns the certificate, CA chain, and (when generated)
        the private key.

    .PARAMETER Csr
        A PEM-encoded certificate signing request for the CA to sign

    .PARAMETER Domain
        One or more domain names (CN/SANs) to issue the certificate for

    .PARAMETER IpAddress
        One or more IP addresses to include as SANs

    .PARAMETER Duration
        Validity in days

    .PARAMETER Provider
        The CA provider to issue from

    .PARAMETER Name
        A name for the certificate

    .EXAMPLE
        New-CSCertificate -Csr (Get-Content ./host.csr -Raw) -Duration 365
        Signs a CSR for one year.

    .EXAMPLE
        $cert = New-CSCertificate -Domain 'host1.cloud.example.com' -IpAddress '10.1.1.20' -Duration 730
        $cert.privatekey | Set-Content ./host1.key
        Has the CA generate a key pair and certificate and saves the private key.
    #>

    [CmdletBinding()]
    param(
        [string]$Csr,

        [string[]]$Domain,

        [string[]]$IpAddress,

        [int]$Duration,

        [string]$Provider,

        [string]$Name
    )

    if (-not $PSBoundParameters.ContainsKey('Csr') -and -not $PSBoundParameters.ContainsKey('Domain')) {
        throw 'Specify -Csr to sign a request, or -Domain to have the CA generate the certificate.'
    }
    $apiParams = @{}
    Add-CSOptionalParameter -ApiParameters $apiParams -BoundParameters $PSBoundParameters -Map ([ordered]@{
        Csr = 'csr'; Domain = 'domain'; IpAddress = 'ipaddress'; Duration = 'duration'; Provider = 'provider'; Name = 'name'
    })
    ConvertFrom-CSResponse -Response (Invoke-CSApiRequest -Command 'issueCertificate' -Parameters $apiParams) -Command 'issueCertificate'
}

function New-CSHostCertificate {
    <#
    .SYNOPSIS
        Issues and provisions a client certificate onto a host.

    .DESCRIPTION
        Wraps provisionCertificate, which issues a fresh client certificate through
        the CA plugin and installs it on a connected host or agent. Use -Reconnect
        to bounce the agent afterwards so it picks up the new certificate. This is an
        asynchronous job; use -Wait to block until it finishes. Accepts host objects
        on the pipeline.

    .PARAMETER HostId
        The host to provision. Binds from a piped host's id.

    .PARAMETER Reconnect
        Reconnect the host's agent after provisioning

    .PARAMETER Provider
        The CA provider to issue the certificate from

    .PARAMETER Wait
        Wait for the async job to finish and return the result

    .EXAMPLE
        New-CSHostCertificate -HostId $hostId -Reconnect -Wait
        Provisions a new certificate on a host and reconnects its agent.

    .EXAMPLE
        Get-CSHost -Name 'kvm-01' | New-CSHostCertificate -Wait
        Provisions a certificate on a host piped in by object.
    #>

    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'Medium')]
    param(
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
        [Alias('Id')]
        [string]$HostId,

        [switch]$Reconnect,

        [string]$Provider,

        [switch]$Wait
    )

    process {
        $apiParams = @{ hostid = $HostId }
        Add-CSOptionalParameter -ApiParameters $apiParams -BoundParameters $PSBoundParameters -Map ([ordered]@{
            Reconnect = 'reconnect'; Provider = 'provider'
        })
        if ($PSCmdlet.ShouldProcess("host $HostId", 'Provision a client certificate')) {
            Invoke-CSAsyncApiRequest -Command 'provisionCertificate' -Parameters $apiParams -Wait:$Wait
        }
    }
}

function Revoke-CSCertificate {
    <#
    .SYNOPSIS
        Revokes a client certificate.

    .DESCRIPTION
        Wraps revokeCertificate, adding the certificate identified by its serial
        number to the CA plugin's revocation list. This is an asynchronous job; use
        -Wait to block until it finishes.

    .PARAMETER Serial
        The serial number of the certificate to revoke

    .PARAMETER Cn
        The certificate's common name (helps the CA locate it)

    .PARAMETER Provider
        The CA provider that issued the certificate

    .PARAMETER Wait
        Wait for the async job to finish and return the result

    .EXAMPLE
        Revoke-CSCertificate -Serial '1a2b3c' -Cn 'kvm-01.cloud.example.com' -Wait
        Revokes a host's certificate.
    #>

    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'High')]
    param(
        [Parameter(Mandatory = $true)]
        [string]$Serial,

        [string]$Cn,

        [string]$Provider,

        [switch]$Wait
    )

    $apiParams = @{ serial = $Serial }
    Add-CSOptionalParameter -ApiParameters $apiParams -BoundParameters $PSBoundParameters -Map ([ordered]@{
        Cn = 'cn'; Provider = 'provider'
    })
    if ($PSCmdlet.ShouldProcess("certificate serial $Serial", 'Revoke')) {
        Invoke-CSAsyncApiRequest -Command 'revokeCertificate' -Parameters $apiParams -Wait:$Wait
    }
}

function Set-CSCustomCertificate {
    <#
    .SYNOPSIS
        Uploads a custom SSL certificate for the console proxy.

    .DESCRIPTION
        Wraps uploadCustomCertificate. Use it to install a CA-signed certificate for
        the console proxy VMs' SSL. A certificate chain is uploaded over several
        calls: the root and intermediate certificates (each with -Name and -Id for
        its position in the chain), then the server certificate with its
        -PrivateKey. This is an asynchronous job; use -Wait to block until it
        finishes.

    .PARAMETER Certificate
        The PEM-encoded certificate to upload

    .PARAMETER PrivateKey
        The PEM-encoded private key, for the server certificate

    .PARAMETER DomainSuffix
        The DNS domain suffix the console proxy certificate is issued for

    .PARAMETER Id
        The certificate's position in the chain (for a multi-certificate chain)

    .PARAMETER Name
        A name for the uploaded certificate (for a CA/intermediate certificate)

    .PARAMETER Wait
        Wait for the async job to finish and return the result

    .EXAMPLE
        Set-CSCustomCertificate -Certificate (Get-Content ./console.crt -Raw) -PrivateKey (Get-Content ./console.key -Raw) -DomainSuffix 'cloud.example.com' -Wait
        Uploads a single console proxy certificate and its key.

    .EXAMPLE
        Set-CSCustomCertificate -Certificate (Get-Content ./root-ca.crt -Raw) -Name root -Id 1 -DomainSuffix 'cloud.example.com' -Wait
        Uploads the root CA as the first certificate in a chain.
    #>

    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'Medium')]
    param(
        [Parameter(Mandatory = $true)]
        [string]$Certificate,

        [string]$PrivateKey,

        [string]$DomainSuffix,

        [string]$Id,

        [string]$Name,

        [switch]$Wait
    )

    $apiParams = @{ certificate = $Certificate }
    Add-CSOptionalParameter -ApiParameters $apiParams -BoundParameters $PSBoundParameters -Map ([ordered]@{
        PrivateKey = 'privatekey'; DomainSuffix = 'domainsuffix'; Id = 'id'; Name = 'name'
    })
    if ($PSCmdlet.ShouldProcess('console proxy SSL certificate', 'Upload')) {
        Invoke-CSAsyncApiRequest -Command 'uploadCustomCertificate' -Parameters $apiParams -Wait:$Wait
    }
}

function Get-CSDirectDownloadCertificate {
    <#
    .SYNOPSIS
        Lists the certificates uploaded for direct-download templates.

    .DESCRIPTION
        Wraps listTemplateDirectDownloadCertificates. These are the HTTPS
        certificates KVM hosts use to fetch direct-download templates. Use
        -ListHosts to include each certificate's per-host provisioning status.

    .PARAMETER ZoneId
        Filter by zone ID

    .PARAMETER Hypervisor
        Filter by hypervisor (typically KVM)

    .PARAMETER ListHosts
        Include the per-host status of each certificate

    .PARAMETER Keyword
        Filter by keyword

    .PARAMETER Page
        Page number of results to return

    .PARAMETER PageSize
        Number of results per page

    .EXAMPLE
        Get-CSDirectDownloadCertificate -ZoneId $zoneId
        Lists the direct-download certificates in a zone.

    .EXAMPLE
        Get-CSDirectDownloadCertificate -ZoneId $zoneId -ListHosts
        Lists them with each certificate's per-host provisioning status.
    #>

    [CmdletBinding()]
    param(
        [Parameter(ValueFromPipelineByPropertyName = $true)]
        [string]$ZoneId,

        [string]$Hypervisor,

        [switch]$ListHosts,

        [string]$Keyword,

        [int]$Page,

        [int]$PageSize
    )

    process {
        $apiParams = @{}
        Add-CSOptionalParameter -ApiParameters $apiParams -BoundParameters $PSBoundParameters -Map ([ordered]@{
            ZoneId = 'zoneid'; Hypervisor = 'hypervisor'; ListHosts = 'listhosts'; Keyword = 'keyword'; Page = 'page'; PageSize = 'pagesize'
        })
        ConvertFrom-CSResponse -Response (Invoke-CSApiRequest -Command 'listTemplateDirectDownloadCertificates' -Parameters $apiParams) -Command 'listTemplateDirectDownloadCertificates'
    }
}

function New-CSDirectDownloadCertificate {
    <#
    .SYNOPSIS
        Uploads a certificate for direct-download template fetches.

    .DESCRIPTION
        Wraps uploadTemplateDirectDownloadCertificate, registering an HTTPS
        certificate that KVM hosts trust when downloading direct-download templates.
        It is provisioned onto the zone's hosts (or just -HostId). Returns the
        per-host upload result.

    .PARAMETER Certificate
        The PEM-encoded certificate to upload

    .PARAMETER Name
        A name (alias) for the certificate

    .PARAMETER Hypervisor
        The hypervisor the certificate is for (typically KVM)

    .PARAMETER ZoneId
        The zone whose hosts should trust the certificate

    .PARAMETER HostId
        Upload to only this host instead of all hosts in the zone

    .EXAMPLE
        New-CSDirectDownloadCertificate -Certificate (Get-Content ./images-ca.crt -Raw) -Name 'images-ca' -Hypervisor KVM -ZoneId $zoneId
        Registers a certificate on every KVM host in a zone.
    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true)]
        [string]$Certificate,

        [Parameter(Mandatory = $true)]
        [string]$Name,

        [Parameter(Mandatory = $true)]
        [string]$Hypervisor,

        [Parameter(Mandatory = $true)]
        [string]$ZoneId,

        [string]$HostId
    )

    $apiParams = @{ certificate = $Certificate; name = $Name; hypervisor = $Hypervisor; zoneid = $ZoneId }
    if ($PSBoundParameters.ContainsKey('HostId')) { $apiParams['hostid'] = $HostId }
    ConvertFrom-CSResponse -Response (Invoke-CSApiRequest -Command 'uploadTemplateDirectDownloadCertificate' -Parameters $apiParams) -Command 'uploadTemplateDirectDownloadCertificate'
}

function Copy-CSDirectDownloadCertificate {
    <#
    .SYNOPSIS
        Provisions an uploaded direct-download certificate onto a host.

    .DESCRIPTION
        Wraps provisionTemplateDirectDownloadCertificate, installing a certificate
        that was already uploaded (see New-CSDirectDownloadCertificate) onto one
        host - useful for a host added after the initial upload. Use -Wait to block
        until it finishes. Accepts certificate objects on the pipeline.

    .PARAMETER Id
        The uploaded certificate to provision. Binds from a piped certificate's id.

    .PARAMETER HostId
        The host to provision the certificate onto

    .PARAMETER Wait
        Wait for the async job to finish and return the result

    .EXAMPLE
        Copy-CSDirectDownloadCertificate -Id $certId -HostId $hostId -Wait
        Installs an existing certificate on a newly added host.

    .EXAMPLE
        Get-CSDirectDownloadCertificate -ZoneId $zoneId | Where-Object name -eq 'images-ca' | Copy-CSDirectDownloadCertificate -HostId $hostId
        Provisions a certificate located by name onto a host.
    #>

    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'Medium')]
    param(
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
        [Alias('CertId')]
        [string]$Id,

        [Parameter(Mandatory = $true)]
        [string]$HostId,

        [switch]$Wait
    )

    process {
        $apiParams = @{ id = $Id; hostid = $HostId }
        if ($PSCmdlet.ShouldProcess("host $HostId", "Provision direct-download certificate $Id")) {
            Invoke-CSAsyncApiRequest -Command 'provisionTemplateDirectDownloadCertificate' -Parameters $apiParams -Wait:$Wait
        }
    }
}

function Revoke-CSDirectDownloadCertificate {
    <#
    .SYNOPSIS
        Revokes a direct-download certificate from a zone's hosts.

    .DESCRIPTION
        Wraps revokeTemplateDirectDownloadCertificate, removing a previously
        uploaded direct-download certificate from the hosts in a zone (or just
        -HostId). Use -Wait to block until it finishes. Accepts certificate objects
        on the pipeline.

    .PARAMETER Id
        The certificate to revoke. Binds from a piped certificate's id.

    .PARAMETER HostId
        Revoke from only this host instead of all hosts

    .PARAMETER Wait
        Wait for the async job to finish and return the result

    .EXAMPLE
        Revoke-CSDirectDownloadCertificate -Id $certId
        Revokes a direct-download certificate from its hosts.

    .EXAMPLE
        Get-CSDirectDownloadCertificate -ZoneId $zoneId | Where-Object name -eq 'old-ca' | Revoke-CSDirectDownloadCertificate
        Revokes a certificate located by name.
    #>

    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'High')]
    param(
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
        [Alias('CertId')]
        [string]$Id,

        [string]$HostId,

        [switch]$Wait
    )

    process {
        $apiParams = @{ id = $Id }
        if ($PSBoundParameters.ContainsKey('HostId')) { $apiParams['hostid'] = $HostId }
        if ($PSCmdlet.ShouldProcess("direct-download certificate $Id", 'Revoke')) {
            Invoke-CSAsyncApiRequest -Command 'revokeTemplateDirectDownloadCertificate' -Parameters $apiParams -Wait:$Wait
        }
    }
}