Public/quota.ps1

# CloudStack Quota plugin: usage tariffs, per-account balances and statements,
# credits, email notifications, and the plugin's own enable/update controls.
# All of these require the Quota plugin to be enabled on the management server.

function Test-CSQuotaEnabled {
    <#
    .SYNOPSIS
        Returns whether the Quota plugin is enabled.

    .DESCRIPTION
        Wraps quotaIsEnabled and returns $true or $false. Every other Quota command
        needs the plugin enabled, so use this first when a quota call fails.

    .EXAMPLE
        Test-CSQuotaEnabled
        Returns $true when quota is enabled on the management server.

    .EXAMPLE
        if (Test-CSQuotaEnabled) { Get-CSQuotaSummary }
        Only queries quota data when the plugin is on.
    #>

    [CmdletBinding()]
    param()

    $result = ConvertFrom-CSResponse -Response (Invoke-CSApiRequest -Command 'quotaIsEnabled' -Parameters @{}) -Command 'quotaIsEnabled'
    return ($result.isenabled -eq $true -or "$($result.isenabled)".ToLowerInvariant() -eq 'true')
}

function Get-CSQuotaBalance {
    <#
    .SYNOPSIS
        Gets an account's quota balance.

    .DESCRIPTION
        Wraps quotaBalance. Identify the owner by -Account plus -DomainId, or by
        -AccountId. With -EndDate the balance is reported as of that date; with both
        dates, the credits over the range are included. Accepts account objects on
        the pipeline.

    .PARAMETER Account
        Account name. Must be used with -DomainId.

    .PARAMETER DomainId
        The account's domain ID

    .PARAMETER AccountId
        The account's ID, as an alternative to -Account/-DomainId

    .PARAMETER ProjectId
        Get the balance for this project

    .PARAMETER StartDate
        Start of the range: a 'yyyy-MM-dd' string or a DateTime

    .PARAMETER EndDate
        Balance as of this date: a 'yyyy-MM-dd' string or a DateTime

    .EXAMPLE
        Get-CSQuotaBalance -Account 'engineering' -DomainId $domainId
        Shows one account's current balance.

    .EXAMPLE
        Get-CSAccount -Name 'engineering' -DomainId $domainId | Get-CSQuotaBalance -EndDate '2026-09-30'
        Pipes an account in and reads its balance at month end.
    #>

    [CmdletBinding()]
    param(
        [Parameter(ValueFromPipelineByPropertyName = $true)]
        [Alias('Name')]
        [string]$Account,

        [Parameter(ValueFromPipelineByPropertyName = $true)]
        [string]$DomainId,

        [Parameter(ValueFromPipelineByPropertyName = $true)]
        [Alias('Id')]
        [string]$AccountId,

        [Parameter(ValueFromPipelineByPropertyName = $true)]
        [string]$ProjectId,

        [object]$StartDate,

        [object]$EndDate
    )

    process {
        if (-not $PSBoundParameters.ContainsKey('Account') -and -not $PSBoundParameters.ContainsKey('AccountId') -and -not $PSBoundParameters.ContainsKey('ProjectId')) {
            throw 'Specify an owner with -Account (plus -DomainId), -AccountId, or -ProjectId.'
        }
        $apiParams = @{}
        Add-CSOptionalParameter -ApiParameters $apiParams -BoundParameters $PSBoundParameters -Map ([ordered]@{
            Account = 'account'; DomainId = 'domainid'; AccountId = 'accountid'; ProjectId = 'projectid'
        })
        if ($PSBoundParameters.ContainsKey('StartDate')) { $apiParams['startdate'] = ConvertTo-CSDateParameter -Value $StartDate -Format 'yyyy-MM-dd' }
        if ($PSBoundParameters.ContainsKey('EndDate')) { $apiParams['enddate'] = ConvertTo-CSDateParameter -Value $EndDate -Format 'yyyy-MM-dd' }
        ConvertFrom-CSResponse -Response (Invoke-CSApiRequest -Command 'quotaBalance' -Parameters $apiParams) -Command 'quotaBalance'
    }
}

function Get-CSQuotaStatement {
    <#
    .SYNOPSIS
        Gets an account's quota usage statement for a date range.

    .DESCRIPTION
        Wraps quotaStatement. Reports quota consumed per usage type between
        -StartDate and -EndDate (both required). Identify the owner by -Account plus
        -DomainId, or by -AccountId. Accepts account objects on the pipeline.

    .PARAMETER StartDate
        Start of the period: a 'yyyy-MM-dd' string or a DateTime

    .PARAMETER EndDate
        End of the period: a 'yyyy-MM-dd' string or a DateTime

    .PARAMETER Account
        Account name. Must be used with -DomainId.

    .PARAMETER DomainId
        The account's domain ID

    .PARAMETER AccountId
        The account's ID, as an alternative to -Account/-DomainId

    .PARAMETER ProjectId
        Get the statement for this project

    .PARAMETER Type
        Only this quota usage type ID

    .EXAMPLE
        Get-CSQuotaStatement -Account 'engineering' -DomainId $domainId -StartDate '2026-09-01' -EndDate '2026-09-30'
        Gets September's quota statement for an account.

    .EXAMPLE
        Get-CSAccount -Name 'engineering' -DomainId $domainId | Get-CSQuotaStatement -StartDate '2026-09-01' -EndDate '2026-09-30'
        Pipes an account in and gets its statement.
    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true)]
        [object]$StartDate,

        [Parameter(Mandatory = $true)]
        [object]$EndDate,

        [Parameter(ValueFromPipelineByPropertyName = $true)]
        [Alias('Name')]
        [string]$Account,

        [Parameter(ValueFromPipelineByPropertyName = $true)]
        [string]$DomainId,

        [Parameter(ValueFromPipelineByPropertyName = $true)]
        [Alias('Id')]
        [string]$AccountId,

        [Parameter(ValueFromPipelineByPropertyName = $true)]
        [string]$ProjectId,

        [int]$Type
    )

    process {
        $apiParams = @{
            startdate = ConvertTo-CSDateParameter -Value $StartDate -Format 'yyyy-MM-dd'
            enddate = ConvertTo-CSDateParameter -Value $EndDate -Format 'yyyy-MM-dd'
        }
        Add-CSOptionalParameter -ApiParameters $apiParams -BoundParameters $PSBoundParameters -Map ([ordered]@{
            Account = 'account'; DomainId = 'domainid'; AccountId = 'accountid'; ProjectId = 'projectid'; Type = 'type'
        })
        ConvertFrom-CSResponse -Response (Invoke-CSApiRequest -Command 'quotaStatement' -Parameters $apiParams) -Command 'quotaStatement'
    }
}

function Get-CSQuotaSummary {
    <#
    .SYNOPSIS
        Lists per-account quota balances and usage summaries.

    .DESCRIPTION
        Wraps quotaSummary. With no filter it summarizes the caller's own account;
        admins can widen it with -DomainId, -Account, or -ListAll. Accepts account
        objects on the pipeline.

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

    .PARAMETER DomainId
        Filter by domain ID

    .PARAMETER ProjectId
        Filter by project ID

    .PARAMETER ListAll
        List every account the caller is allowed to see

    .PARAMETER Keyword
        Filter by keyword

    .PARAMETER Page
        Page number of results to return

    .PARAMETER PageSize
        Number of results per page

    .EXAMPLE
        Get-CSQuotaSummary -ListAll
        Lists quota balances for every visible account.

    .EXAMPLE
        Get-CSQuotaSummary -DomainId $domainId | Where-Object quota -lt 10
        Finds accounts in a domain with a low quota balance.
    #>

    [CmdletBinding()]
    param(
        [Parameter(ValueFromPipelineByPropertyName = $true)]
        [Alias('Name')]
        [string]$Account,

        [Parameter(ValueFromPipelineByPropertyName = $true)]
        [string]$DomainId,

        [Parameter(ValueFromPipelineByPropertyName = $true)]
        [string]$ProjectId,

        [switch]$ListAll,

        [string]$Keyword,

        [int]$Page,

        [int]$PageSize
    )

    process {
        $apiParams = @{}
        Add-CSOptionalParameter -ApiParameters $apiParams -BoundParameters $PSBoundParameters -Map ([ordered]@{
            Account = 'account'; DomainId = 'domainid'; ProjectId = 'projectid'; ListAll = 'listall'; Keyword = 'keyword'; Page = 'page'; PageSize = 'pagesize'
        })
        ConvertFrom-CSResponse -Response (Invoke-CSApiRequest -Command 'quotaSummary' -Parameters $apiParams) -Command 'quotaSummary'
    }
}

function Add-CSQuotaCredit {
    <#
    .SYNOPSIS
        Adds (or deducts) quota credit for an account.

    .DESCRIPTION
        Wraps quotaCredits. A positive -Value adds credit, a negative one deducts
        it. -QuotaEnforce turns balance enforcement on or off for the account, and
        -MinBalance sets the threshold that triggers a low-balance email. Accepts
        account objects on the pipeline.

    .PARAMETER Account
        Account name to credit. Must be used with -DomainId.

    .PARAMETER DomainId
        The account's domain ID

    .PARAMETER Value
        Amount of credit to add (negative to deduct)

    .PARAMETER MinBalance
        Balance threshold below which a low-balance email is sent

    .PARAMETER QuotaEnforce
        Whether to enforce the quota balance for this account

    .EXAMPLE
        Add-CSQuotaCredit -Account 'engineering' -DomainId $domainId -Value 500
        Adds 500 quota credits to an account.

    .EXAMPLE
        Get-CSAccount -Name 'engineering' -DomainId $domainId | Add-CSQuotaCredit -Value 1000 -MinBalance 50 -QuotaEnforce $true
        Tops up an account and turns on enforcement with a low-balance alert.
    #>

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

        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
        [string]$DomainId,

        [Parameter(Mandatory = $true)]
        [double]$Value,

        [double]$MinBalance,

        [bool]$QuotaEnforce
    )

    process {
        $apiParams = @{ account = $Account; domainid = $DomainId; value = $Value }
        Add-CSOptionalParameter -ApiParameters $apiParams -BoundParameters $PSBoundParameters -Map ([ordered]@{
            MinBalance = 'min_balance'; QuotaEnforce = 'quota_enforce'
        })
        $verb = if ($Value -lt 0) { 'Deduct' } else { 'Add' }
        if ($PSCmdlet.ShouldProcess("account $Account", "$verb $Value quota credit")) {
            ConvertFrom-CSResponse -Response (Invoke-CSApiRequest -Command 'quotaCredits' -Parameters $apiParams) -Command 'quotaCredits'
        }
    }
}

function Update-CSQuota {
    <#
    .SYNOPSIS
        Runs the quota calculation over the latest usage.

    .DESCRIPTION
        Wraps quotaUpdate, which processes new usage records into quota balances.
        The scheduled quota job normally does this; call it to force a run.

    .EXAMPLE
        Update-CSQuota
        Recalculates quota balances from the newest usage now.
    #>

    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'Medium')]
    param()

    if ($PSCmdlet.ShouldProcess('quota balances', 'Recalculate from usage')) {
        ConvertFrom-CSResponse -Response (Invoke-CSApiRequest -Command 'quotaUpdate' -Parameters @{}) -Command 'quotaUpdate'
    }
}

function Get-CSQuotaTariff {
    <#
    .SYNOPSIS
        Lists quota tariffs (the price of each usage type).

    .DESCRIPTION
        Wraps quotaTariffList. A tariff sets how many quota credits a unit of a
        usage type costs. Filter by -UsageType, or by -Id or -Name for one tariff,
        and use the dates to see tariffs effective in a period.

    .PARAMETER Id
        Filter by tariff ID

    .PARAMETER Name
        Filter by tariff name

    .PARAMETER UsageType
        Filter by quota usage type ID

    .PARAMETER StartDate
        Only tariffs effective on or after this date: a 'yyyy-MM-dd' string or DateTime

    .PARAMETER EndDate
        Only tariffs effective on or before this date: a string or DateTime

    .PARAMETER ListAll
        Include tariffs that are no longer active

    .PARAMETER Keyword
        Filter by keyword

    .PARAMETER Page
        Page number of results to return

    .PARAMETER PageSize
        Number of results per page

    .EXAMPLE
        Get-CSQuotaTariff
        Lists the currently effective tariffs.

    .EXAMPLE
        Get-CSQuotaTariff -UsageType 1 -ListAll
        Lists every tariff (active or not) for one usage type.
    #>

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

        [string]$Name,

        [int]$UsageType,

        [object]$StartDate,

        [object]$EndDate,

        [switch]$ListAll,

        [string]$Keyword,

        [int]$Page,

        [int]$PageSize
    )

    $apiParams = @{}
    Add-CSOptionalParameter -ApiParameters $apiParams -BoundParameters $PSBoundParameters -Map ([ordered]@{
        Id = 'id'; Name = 'name'; UsageType = 'usagetype'; ListAll = 'listall'; Keyword = 'keyword'; Page = 'page'; PageSize = 'pagesize'
    })
    if ($PSBoundParameters.ContainsKey('StartDate')) { $apiParams['startdate'] = ConvertTo-CSDateParameter -Value $StartDate -Format 'yyyy-MM-dd' }
    if ($PSBoundParameters.ContainsKey('EndDate')) { $apiParams['enddate'] = ConvertTo-CSDateParameter -Value $EndDate -Format 'yyyy-MM-dd' }
    ConvertFrom-CSResponse -Response (Invoke-CSApiRequest -Command 'quotaTariffList' -Parameters $apiParams) -Command 'quotaTariffList'
}

function New-CSQuotaTariff {
    <#
    .SYNOPSIS
        Creates a quota tariff for a usage type.

    .DESCRIPTION
        Wraps quotaTariffCreate. -Value is the quota cost per unit of the usage
        type. -ActivationRule is an optional JavaScript expression that decides when
        the tariff applies (see Get-CSQuotaPresetVariable for the variables it can
        use). Use -StartDate to schedule it and -Position to order overlapping
        tariffs.

    .PARAMETER Name
        A unique name for the tariff

    .PARAMETER Value
        Quota cost per unit of the usage type

    .PARAMETER UsageType
        The quota usage type ID the tariff prices

    .PARAMETER StartDate
        Date the tariff takes effect: a 'yyyy-MM-dd' string or a DateTime

    .PARAMETER EndDate
        Date the tariff stops applying: a 'yyyy-MM-dd' string or a DateTime

    .PARAMETER Description
        A description of the tariff

    .PARAMETER ActivationRule
        JavaScript expression controlling when the tariff applies

    .PARAMETER Position
        Order in which this tariff is evaluated against others

    .EXAMPLE
        New-CSQuotaTariff -Name 'running-vm-standard' -Value 2.5 -UsageType 1 -StartDate '2026-10-01'
        Prices running-VM hours at 2.5 credits from October.

    .EXAMPLE
        New-CSQuotaTariff -Name 'premium-storage' -Value 0.10 -UsageType 6 -ActivationRule 'value.storageTags.includes("ssd")'
        Adds a storage tariff that only applies to SSD-tagged storage.
    #>

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

        [Parameter(Mandatory = $true)]
        [double]$Value,

        [Parameter(Mandatory = $true)]
        [int]$UsageType,

        [object]$StartDate,

        [object]$EndDate,

        [string]$Description,

        [string]$ActivationRule,

        [int]$Position
    )

    $apiParams = @{ name = $Name; value = $Value; usagetype = $UsageType }
    Add-CSOptionalParameter -ApiParameters $apiParams -BoundParameters $PSBoundParameters -Map ([ordered]@{
        Description = 'description'; ActivationRule = 'activationrule'; Position = 'position'
    })
    if ($PSBoundParameters.ContainsKey('StartDate')) { $apiParams['startdate'] = ConvertTo-CSDateParameter -Value $StartDate -Format 'yyyy-MM-dd' }
    if ($PSBoundParameters.ContainsKey('EndDate')) { $apiParams['enddate'] = ConvertTo-CSDateParameter -Value $EndDate -Format 'yyyy-MM-dd' }

    if ($PSCmdlet.ShouldProcess("quota tariff $Name", 'Create')) {
        ConvertFrom-CSResponse -Response (Invoke-CSApiRequest -Command 'quotaTariffCreate' -Parameters $apiParams) -Command 'quotaTariffCreate'
    }
}

function Set-CSQuotaTariff {
    <#
    .SYNOPSIS
        Updates a quota tariff.

    .DESCRIPTION
        Wraps quotaTariffUpdate. The tariff is identified by -Name; supply the
        attributes you want to change. CloudStack records the change as a new
        effective version rather than editing history. Accepts tariff objects (which
        carry a name) on the pipeline.

    .PARAMETER Name
        Name of the tariff to update. Binds from a piped tariff's name.

    .PARAMETER Value
        New quota cost per unit

    .PARAMETER StartDate
        New effective date: a 'yyyy-MM-dd' string or a DateTime

    .PARAMETER EndDate
        New end date: a 'yyyy-MM-dd' string or a DateTime

    .PARAMETER Description
        New description

    .PARAMETER ActivationRule
        New JavaScript activation expression

    .PARAMETER Position
        New evaluation order

    .EXAMPLE
        Set-CSQuotaTariff -Name 'running-vm-standard' -Value 3.0 -StartDate '2026-11-01'
        Raises a tariff's rate effective in November.

    .EXAMPLE
        Get-CSQuotaTariff -Name 'premium-storage' | Set-CSQuotaTariff -EndDate '2026-12-31'
        Pipes a tariff in and schedules it to end.
    #>

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

        [double]$Value,

        [object]$StartDate,

        [object]$EndDate,

        [string]$Description,

        [string]$ActivationRule,

        [int]$Position
    )

    process {
        $apiParams = @{ name = $Name }
        Add-CSOptionalParameter -ApiParameters $apiParams -BoundParameters $PSBoundParameters -Map ([ordered]@{
            Value = 'value'; Description = 'description'; ActivationRule = 'activationrule'; Position = 'position'
        })
        if ($PSBoundParameters.ContainsKey('StartDate')) { $apiParams['startdate'] = ConvertTo-CSDateParameter -Value $StartDate -Format 'yyyy-MM-dd' }
        if ($PSBoundParameters.ContainsKey('EndDate')) { $apiParams['enddate'] = ConvertTo-CSDateParameter -Value $EndDate -Format 'yyyy-MM-dd' }

        if ($PSCmdlet.ShouldProcess("quota tariff $Name", 'Update')) {
            ConvertFrom-CSResponse -Response (Invoke-CSApiRequest -Command 'quotaTariffUpdate' -Parameters $apiParams) -Command 'quotaTariffUpdate'
        }
    }
}

function Remove-CSQuotaTariff {
    <#
    .SYNOPSIS
        Deletes a quota tariff.

    .DESCRIPTION
        Wraps quotaTariffDelete. Removes a tariff by its ID so it no longer prices
        its usage type. Accepts tariff objects on the pipeline.

    .PARAMETER Id
        The tariff ID to delete. Binds from a piped tariff's id.

    .EXAMPLE
        Remove-CSQuotaTariff -Id $tariffId
        Deletes one tariff after confirmation.

    .EXAMPLE
        Get-CSQuotaTariff -Name 'premium-storage' -ListAll | Remove-CSQuotaTariff
        Pipes a tariff in and deletes it.
    #>

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

    process {
        if ($PSCmdlet.ShouldProcess("quota tariff $Id", 'Delete')) {
            ConvertFrom-CSResponse -Response (Invoke-CSApiRequest -Command 'quotaTariffDelete' -Parameters @{ id = $Id }) -Command 'quotaTariffDelete'
        }
    }
}

function Get-CSQuotaPresetVariable {
    <#
    .SYNOPSIS
        Lists the preset variables available to tariff activation rules.

    .DESCRIPTION
        Wraps quotaPresetVariablesList. These are the variables (such as account,
        zone, and resource attributes) that a tariff's -ActivationRule JavaScript
        can reference.

    .EXAMPLE
        Get-CSQuotaPresetVariable
        Lists every variable usable in a tariff activation rule.
    #>

    [CmdletBinding()]
    param()

    ConvertFrom-CSResponse -Response (Invoke-CSApiRequest -Command 'quotaPresetVariablesList' -Parameters @{}) -Command 'quotaPresetVariablesList'
}

function Get-CSQuotaEmailConfiguration {
    <#
    .SYNOPSIS
        Lists an account's quota email notification settings.

    .DESCRIPTION
        Wraps quotaListEmailConfiguration, showing which quota notification emails
        are enabled for an account and their thresholds. Accepts account objects on
        the pipeline. Pairs with Set-CSQuotaEmailConfiguration.

    .PARAMETER AccountId
        The account to read. Binds from a piped account's id.

    .PARAMETER TemplateName
        Only this notification: QUOTA_LOW, QUOTA_EMPTY, or QUOTA_UNLOCK_ACCOUNT

    .EXAMPLE
        Get-CSQuotaEmailConfiguration -AccountId $accountId
        Shows an account's quota email settings.

    .EXAMPLE
        Get-CSAccount -Name 'engineering' -DomainId $domainId | Get-CSQuotaEmailConfiguration
        Reads the settings for an account piped in.
    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
        [Alias('Id')]
        [string]$AccountId,

        [ValidateSet('QUOTA_LOW', 'QUOTA_EMPTY', 'QUOTA_UNLOCK_ACCOUNT')]
        [string]$TemplateName
    )

    process {
        $apiParams = @{ accountid = $AccountId }
        if ($PSBoundParameters.ContainsKey('TemplateName')) { $apiParams['templatename'] = $TemplateName }
        ConvertFrom-CSResponse -Response (Invoke-CSApiRequest -Command 'quotaListEmailConfiguration' -Parameters $apiParams) -Command 'quotaListEmailConfiguration'
    }
}

function Set-CSQuotaEmailConfiguration {
    <#
    .SYNOPSIS
        Enables or disables a quota notification email for an account.

    .DESCRIPTION
        Wraps quotaConfigureEmail. Turns one of the quota email templates on or off
        for an account, and (for the low-balance email) sets the balance threshold
        that triggers it. Accepts account objects on the pipeline.

    .PARAMETER AccountId
        The account to configure. Binds from a piped account's id.

    .PARAMETER TemplateName
        Which notification: QUOTA_LOW, QUOTA_EMPTY, or QUOTA_UNLOCK_ACCOUNT

    .PARAMETER Enable
        Whether the notification is enabled

    .PARAMETER MinBalance
        For QUOTA_LOW, the balance below which the email is sent

    .EXAMPLE
        Set-CSQuotaEmailConfiguration -AccountId $accountId -TemplateName QUOTA_LOW -Enable $true -MinBalance 100
        Turns on the low-balance email at a threshold of 100 credits.

    .EXAMPLE
        Get-CSAccount -Name 'engineering' -DomainId $domainId | Set-CSQuotaEmailConfiguration -TemplateName QUOTA_EMPTY -Enable $false
        Disables the empty-balance email for an account piped in.
    #>

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

        [Parameter(Mandatory = $true)]
        [ValidateSet('QUOTA_LOW', 'QUOTA_EMPTY', 'QUOTA_UNLOCK_ACCOUNT')]
        [string]$TemplateName,

        [Parameter(Mandatory = $true)]
        [bool]$Enable,

        [double]$MinBalance
    )

    process {
        $apiParams = @{ accountid = $AccountId; templatename = $TemplateName; enable = $Enable.ToString().ToLowerInvariant() }
        if ($PSBoundParameters.ContainsKey('MinBalance')) { $apiParams['minbalance'] = $MinBalance }
        $state = if ($Enable) { 'Enable' } else { 'Disable' }
        if ($PSCmdlet.ShouldProcess("account $AccountId", "$state $TemplateName email")) {
            ConvertFrom-CSResponse -Response (Invoke-CSApiRequest -Command 'quotaConfigureEmail' -Parameters $apiParams) -Command 'quotaConfigureEmail'
        }
    }
}

function Get-CSQuotaEmailTemplate {
    <#
    .SYNOPSIS
        Lists the quota notification email templates.

    .DESCRIPTION
        Wraps quotaEmailTemplateList. Returns the subject and body of the quota
        emails, optionally filtered to one -TemplateType.

    .PARAMETER TemplateType
        Only this template: QUOTA_LOW, QUOTA_EMPTY, or QUOTA_UNLOCK_ACCOUNT

    .EXAMPLE
        Get-CSQuotaEmailTemplate
        Lists all quota email templates.

    .EXAMPLE
        Get-CSQuotaEmailTemplate -TemplateType QUOTA_LOW
        Shows the low-balance email's current subject and body.
    #>

    [CmdletBinding()]
    param(
        [ValidateSet('QUOTA_LOW', 'QUOTA_EMPTY', 'QUOTA_UNLOCK_ACCOUNT')]
        [string]$TemplateType
    )

    $apiParams = @{}
    if ($PSBoundParameters.ContainsKey('TemplateType')) { $apiParams['templatetype'] = $TemplateType }
    ConvertFrom-CSResponse -Response (Invoke-CSApiRequest -Command 'quotaEmailTemplateList' -Parameters $apiParams) -Command 'quotaEmailTemplateList'
}

function Set-CSQuotaEmailTemplate {
    <#
    .SYNOPSIS
        Updates a quota notification email template.

    .DESCRIPTION
        Wraps quotaEmailTemplateUpdate. Sets the subject and/or body of one quota
        email. The body may contain the quota template placeholders (see the
        template's current text from Get-CSQuotaEmailTemplate).

    .PARAMETER TemplateType
        Which template to update: QUOTA_LOW, QUOTA_EMPTY, or QUOTA_UNLOCK_ACCOUNT

    .PARAMETER Subject
        New email subject

    .PARAMETER Body
        New email body

    .EXAMPLE
        Set-CSQuotaEmailTemplate -TemplateType QUOTA_LOW -Subject 'Your cloud quota is running low'
        Changes the low-balance email subject.

    .EXAMPLE
        Set-CSQuotaEmailTemplate -TemplateType QUOTA_EMPTY -Body (Get-Content ./quota-empty.html -Raw)
        Replaces the empty-balance email body from a file.
    #>

    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'Low')]
    param(
        [Parameter(Mandatory = $true)]
        [ValidateSet('QUOTA_LOW', 'QUOTA_EMPTY', 'QUOTA_UNLOCK_ACCOUNT')]
        [string]$TemplateType,

        [string]$Subject,

        [string]$Body
    )

    if (-not $PSBoundParameters.ContainsKey('Subject') -and -not $PSBoundParameters.ContainsKey('Body')) {
        throw 'Specify -Subject, -Body, or both.'
    }
    $apiParams = @{ templatetype = $TemplateType }
    Add-CSOptionalParameter -ApiParameters $apiParams -BoundParameters $PSBoundParameters -Map ([ordered]@{
        Subject = 'templatesubject'; Body = 'templatebody'
    })
    if ($PSCmdlet.ShouldProcess("$TemplateType email template", 'Update')) {
        ConvertFrom-CSResponse -Response (Invoke-CSApiRequest -Command 'quotaEmailTemplateUpdate' -Parameters $apiParams) -Command 'quotaEmailTemplateUpdate'
    }
}