Public/usage.ps1

function Get-CSUsageType {
    <#
    .SYNOPSIS
        Lists the usage record types.

    .DESCRIPTION
        Wraps listUsageTypes. Each type's ID is what Get-CSUsageRecord -Type takes;
        pipe a type straight into Get-CSUsageRecord to filter by it.

    .EXAMPLE
        Get-CSUsageType
        Lists every usage type with its ID and description.

    .EXAMPLE
        Get-CSUsageType | Where-Object description -match 'Running VM' | Get-CSUsageRecord -StartDate '2026-09-01' -EndDate '2026-09-30'
        Gets September's running-VM usage records.
    #>

    [CmdletBinding()]
    param()

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

function Get-CSUsageRecord {
    <#
    .SYNOPSIS
        Lists usage records for a date range.

    .DESCRIPTION
        Wraps listUsageRecords (requires the usage server). -StartDate and
        -EndDate take either a string, passed to CloudStack unchanged, or a
        DateTime:
          - A date-only string follows CloudStack's rules: '2026-09-30' as an end
            date means the end of that day, as a start date the beginning.
          - A DateTime is sent with its UTC offset, so (Get-Date).AddDays(-7)
            means exactly that moment regardless of the server's time zone.
        Accepts usage type objects from Get-CSUsageType on the pipeline.

    .PARAMETER StartDate
        Start of the period: a string such as '2026-09-01' or '2026-09-01 08:00:00', or a DateTime

    .PARAMETER EndDate
        End of the period: a string such as '2026-09-30', or a DateTime

    .PARAMETER Type
        Only records of this usage type ID (see Get-CSUsageType). Binds from a
        piped usage type's id.

    .PARAMETER UsageId
        Only records for this resource UUID. Must be used with -Type.

    .PARAMETER Account
        Filter by account name

    .PARAMETER AccountId
        Filter by account ID

    .PARAMETER DomainId
        Filter by domain ID

    .PARAMETER ProjectId
        Filter by project ID

    .PARAMETER IsRecursive
        With -DomainId, include records from subdomains

    .PARAMETER IncludeTags
        Include each resource's tags in the records

    .PARAMETER OldFormat
        Put internal database IDs in the description instead of UUIDs

    .PARAMETER Keyword
        Filter by keyword

    .PARAMETER Page
        Page number of results to return

    .PARAMETER PageSize
        Number of results per page

    .EXAMPLE
        Get-CSUsageRecord -StartDate '2026-09-01' -EndDate '2026-09-30' -Account 'engineering' -DomainId domain-uuid
        Gets one account's usage for September.

    .EXAMPLE
        Get-CSUsageRecord -StartDate (Get-Date).AddDays(-1) -EndDate (Get-Date) -Type 1 |
            Group-Object account | Select-Object Name, @{ n = 'Hours'; e = { ($_.Group | Measure-Object rawusage -Sum).Sum } }
        Totals the last 24 hours of running-VM hours (usage type 1) per account.

    .EXAMPLE
        Get-CSUsageRecord -StartDate '2026-09-01' -EndDate '2026-09-30' -Type 6 -UsageId vol-uuid
        Gets September's usage records for one volume (usage type 6).
    #>

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

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

        [Parameter(ValueFromPipelineByPropertyName = $true)]
        [Alias('Id', 'UsageTypeId')]
        [int]$Type,

        [string]$UsageId,

        [string]$Account,

        [string]$AccountId,

        [string]$DomainId,

        [string]$ProjectId,

        [switch]$IsRecursive,

        [switch]$IncludeTags,

        [switch]$OldFormat,

        [string]$Keyword,

        [int]$Page,

        [int]$PageSize
    )

    process {
        if ($PSBoundParameters.ContainsKey('UsageId') -and -not $PSBoundParameters.ContainsKey('Type')) {
            throw 'UsageId can only be used together with -Type.'
        }
        $apiParams = @{
            startdate = ConvertTo-CSDateParameter -Value $StartDate
            enddate = ConvertTo-CSDateParameter -Value $EndDate
        }
        Add-CSOptionalParameter -ApiParameters $apiParams -BoundParameters $PSBoundParameters -Map ([ordered]@{
            Type = 'type'; UsageId = 'usageid'; Account = 'account'; AccountId = 'accountid'; DomainId = 'domainid'
            ProjectId = 'projectid'; IsRecursive = 'isrecursive'; IncludeTags = 'includetags'; OldFormat = 'oldformat'
            Keyword = 'keyword'; Page = 'page'; PageSize = 'pagesize'
        })
        ConvertFrom-CSResponse -Response (Invoke-CSApiRequest -Command 'listUsageRecords' -Parameters $apiParams) -Command 'listUsageRecords'
    }
}

function Update-CSUsageRecord {
    <#
    .SYNOPSIS
        Generates usage records the scheduled usage job missed.

    .DESCRIPTION
        Wraps generateUsageRecords. CloudStack only generates records for periods
        where the scheduled usage job did not run or failed, so re-running this for
        a period that is already complete does nothing. Dates are whole days; a
        DateTime is sent as its date (yyyy-MM-dd).

    .PARAMETER StartDate
        First day to generate records for: a 'yyyy-MM-dd' string or a DateTime

    .PARAMETER EndDate
        Last day to generate records for: a 'yyyy-MM-dd' string or a DateTime

    .PARAMETER DomainId
        Only generate records for this domain

    .EXAMPLE
        Update-CSUsageRecord -StartDate '2026-09-01' -EndDate '2026-09-30'
        Fills in any usage records missing for September.

    .EXAMPLE
        Update-CSUsageRecord -StartDate (Get-Date).AddDays(-2) -EndDate (Get-Date).AddDays(-1)
        Catches up after the usage server was down for the last two days.
    #>

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

        [object]$EndDate,

        [string]$DomainId
    )

    $apiParams = @{}
    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 ($PSBoundParameters.ContainsKey('DomainId')) { $apiParams['domainid'] = $DomainId }

    $period = if ($apiParams.startdate -or $apiParams.enddate) { "$($apiParams.startdate) to $($apiParams.enddate)" } else { 'all pending periods' }
    if ($PSCmdlet.ShouldProcess("usage records ($period)", 'Generate')) {
        ConvertFrom-CSResponse -Response (Invoke-CSApiRequest -Command 'generateUsageRecords' -Parameters $apiParams) -Command 'generateUsageRecords'
    }
}

function Remove-CSRawUsageRecord {
    <#
    .SYNOPSIS
        Purges old raw usage records from the usage database.

    .DESCRIPTION
        Wraps removeRawUsageRecords, which deletes raw records older than -Interval
        days from the cloud_usage database to keep it small. Aggregated usage
        records already generated from them are kept, but the raw data cannot be
        reprocessed afterwards.

    .PARAMETER Interval
        Delete raw records older than this many days (must be greater than 0)

    .EXAMPLE
        Remove-CSRawUsageRecord -Interval 90
        Deletes raw usage records older than 90 days after prompting for confirmation.

    .EXAMPLE
        Remove-CSRawUsageRecord -Interval 365 -WhatIf
        Shows what purging raw records older than a year would do, without doing it.
    #>

    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'High')]
    param(
        [Parameter(Mandatory = $true)]
        [ValidateRange(1, [int]::MaxValue)]
        [int]$Interval
    )

    if ($PSCmdlet.ShouldProcess("raw usage records older than $Interval days", 'Delete')) {
        ConvertFrom-CSResponse -Response (Invoke-CSApiRequest -Command 'removeRawUsageRecords' -Parameters @{ interval = $Interval }) -Command 'removeRawUsageRecords'
    }
}