Private/Export-CitrixUsageMetrics.ps1

<#
.SYNOPSIS
Exports Citrix session usage metrics, including peak sessions, login time, and machine counts per Delivery Group, to a JSON file.
 
.DESCRIPTION
The Export-CitrixUsageMetrics function collects and exports usage analytics for specified Citrix Delivery Controllers over a specified date range.
It queries data such as session summaries, failure logs, and machine counts via the Citrix OData Monitor service and outputs the data to a JSON file.
 
The JSON file is named using the format: `CitrixMonitorData-[Environment]-[yyyy_MM].json`.
 
.PARAMETER DeliveryControllers
One or more Citrix Delivery Controller hostnames to retrieve data from. Required.
 
.PARAMETER StartDate
The beginning of the reporting period. Defaults to the previous day at 00:00:00.
 
.PARAMETER EndDate
The end of the reporting period. Defaults to the previous day at 23:59:59.
 
.PARAMETER ExportFolder
The folder path where the final JSON output should be saved. Defaults to `C:\temp`.
 
.EXAMPLE
Export-CitrixUsageMetrics -DeliveryControllers "ddc001", "ddc001"
 
Exports usage metrics for the previous day from the specified delivery controllers and saves the results in `C:\temp`.
 
.EXAMPLE
Export-CitrixUsageMetrics -DeliveryControllers "ddc001" -StartDate "2025-03-01" -EndDate "2025-03-31" -ExportFolder "D:\Exports"
 
Exports usage metrics for March 2025 and saves them to the specified export directory.
 
.OUTPUTS
[PSCustomObject[]]
A list of delivery group metrics including:
- DeliveryController
- DeliveryGroup
- MaxSessions
- AvgLogonTimeSec
- FailureCount
- MachineCount
 
.NOTES
- Only active delivery groups with at least one machine are included.
- Session summary granularity is adjusted based on the date range.
- Requires the Citrix Monitor OData API to be accessible from the executing system.
#>


function Export-CitrixUsageMetrics {
    [CmdletBinding()]
    [OutputType('PSCustomObject')]
    param(
        [Parameter(Mandatory = $true)]
        [String[]]$DeliveryControllers,

        [Parameter()]
        [DateTime]$StartDate = (Get-Date).AddDays(-1).Date,

        [Parameter()]
        [DateTime]$EndDate = (Get-Date).AddDays(-1).Date.AddHours(23).AddMinutes(59).AddSeconds(59),

        [Parameter()]
        [string]$ExportFolder = "C:\\temp"
    )

    $finalResult = @()
    $OutputObjects = @()

    foreach ($DeliveryController in $DeliveryControllers) {
        Write-Host "Querying data for Delivery Controller: $DeliveryController"

        $EnvironmentPrefix = if ($DeliveryController -match 'VAUS') { 'DR' } else { 'Prod' }

        function Invoke-CitrixODataQuery {
            param(
                [string]$DeliveryController,
                [string]$Endpoint,
                [string]$Query = ''
            )

            $uri = "http://$DeliveryController/Citrix/Monitor/OData/v3/Data/$Endpoint$Query"
            $params = @{ Uri = $uri; Method = 'GET'; UseDefaultCredentials = $true; ErrorAction = 'Stop' }

            try {
                return Invoke-RestMethod @params
            } catch {
                Write-LogEntry -Message "Failed to query $Endpoint$Query on $DeliveryController : $_"
                return [PSCustomObject]@{ value = @() }
            }
        }

        $machines = Invoke-CitrixODataQuery -DeliveryController $DeliveryController -Endpoint 'Machines' -Query "?`$format=json&`$select=HostedMachineId,DesktopGroupId&`$filter=(LifecycleState eq 0) and (DesktopGroupId ne null) and (HostedMachineId ne null)"

        $deliveryGroups = Invoke-CitrixODataQuery -DeliveryController $DeliveryController -Endpoint 'DesktopGroups' -Query '?$format=json&$select=Id,Name'
        $activeGroupIds = $machines.value | Select-Object -ExpandProperty DesktopGroupId -Unique
        $deliveryGroups.value = $deliveryGroups.value | Where-Object { $activeGroupIds -contains $_.Id }
        Write-LogEntry -Message "Total Active Delivery Groups on $DeliveryController : $($deliveryGroups.value.Count)"

        $granularity = if ((New-TimeSpan -Start $StartDate -End $EndDate).TotalSeconds -le 3600) { '1' } elseif ((New-TimeSpan -Start $StartDate -End $EndDate).TotalSeconds -le 2592000) { '60' } else { '1440' }
        $sessionQuery = "?`$format=json&`$filter=(SummaryDate gt DateTime'$($StartDate.ToString('yyyy-MM-ddTHH:mm:ss'))') and (SummaryDate lt DateTime'$($EndDate.ToString('yyyy-MM-ddTHH:mm:ss'))') and (Granularity eq $granularity)"
        $sessions = Invoke-CitrixODataQuery -DeliveryController $DeliveryController -Endpoint 'SessionActivitySummaries' -Query $sessionQuery

        $failuresQuery = "?`$format=json&`$select=DesktopGroupId,FailureCount&`$filter=(SummaryDate gt DateTime'$($StartDate.ToString('yyyy-MM-ddTHH:mm:ss'))') and (SummaryDate lt DateTime'$($EndDate.ToString('yyyy-MM-ddTHH:mm:ss'))') and (FailureCategory eq 1) and (Granularity eq $granularity)"
        $failures = Invoke-CitrixODataQuery -DeliveryController $DeliveryController -Endpoint 'FailureLogSummaries' -Query $failuresQuery

        foreach ($dg in $deliveryGroups.value) {
            $dgId = $dg.Id
            $dgName = $dg.Name

            $maxSessions = ($sessions.value | Where-Object { $_.DesktopGroupId -eq $dgId } | Sort-Object -Property ConcurrentSessionCount -Descending | Select-Object -ExpandProperty ConcurrentSessionCount -First 1)
            if (-not $maxSessions) { $maxSessions = 0 }

            $logonCount = ($sessions.value | Where-Object { $_.DesktopGroupId -eq $dgId } | Measure-Object -Property TotalLogOnCount -Sum).Sum
            $logonDuration = ($sessions.value | Where-Object { $_.DesktopGroupId -eq $dgId -and $_.TotalLogOnCount -ge 1 } | Measure-Object -Property TotalLogOnDuration -Sum).Sum
            $avgLogon = if ($logonCount -gt 0 -and $logonDuration) { [int]($logonDuration / $logonCount / 1000) } else { 0 }

            $failureCount = ($failures.value | Where-Object { $_.DesktopGroupId -eq $dgId } | Measure-Object -Property FailureCount -Sum).Sum
            if (-not $failureCount) { $failureCount = 0 }

            $machineCount = ($machines.value | Where-Object { $_.DesktopGroupId -eq $dgId } | Select-Object -ExpandProperty HostedMachineId -Unique | Measure-Object).Count

            if ($machineCount -gt 0) {
                $finalResult += [PSCustomObject]@{
                    DeliveryController = $DeliveryController
                    DeliveryGroup     = $dgName
                    MaxSessions       = $maxSessions
                    AvgLogonTimeSec   = $avgLogon
                    FailureCount      = $failureCount
                    MachineCount      = $machineCount
                }
            }
        }

        $Output = [PSCustomObject]@{
            StartDate      = $StartDate.ToString("yyyy-MM-ddTHH:mm:ss")
            EndDate        = $EndDate.ToString("yyyy-MM-ddTHH:mm:ss")
            Deployed       = $EnvironmentPrefix
            DeliveryGroups = $finalResult | Where-Object { $_.DeliveryController -eq $DeliveryController } | Sort-Object -Property MaxSessions -Descending
        }

        $OutputObjects += $Output
    }

    if (-not (Test-Path $ExportFolder)) {
        New-Item -Path $ExportFolder -ItemType Directory | Out-Null
    }

    $monthYear = (Get-Date $StartDate).ToString("yyyy_MM")
    $envType = ($OutputObjects.Deployed | Select-Object -First 1)
    $finalPath = Join-Path $ExportFolder "CitrixMonitorData-$envType-$monthYear.json"

    $OutputObjects | ConvertTo-Json -Depth 5 | Out-File -FilePath $finalPath -Encoding UTF8
    Write-LogEntry -Message "JSON export completed: $finalPath"

    return $finalResult | Sort-Object -Property MaxSessions -Descending
}