FinOpsToolkitExt.BenefitRecommendations.psm1

function Get-BenefitRecommendation {
    <#
    .SYNOPSIS
        Queries the Azure Cost Management Benefit Recommendations API.
 
    .DESCRIPTION
        Retrieves savings plan purchase recommendations for a billing account.
        Uses Invoke-AzRestMethod for authentication (requires Connect-AzAccount).
 
    .PARAMETER BillingAccountId
        The billing account ID (e.g., "90846601").
 
    .PARAMETER LookBackPeriod
        The look-back period for usage analysis. Default: Last60Days.
 
    .PARAMETER Term
        The commitment term. Default: P3Y.
 
    .PARAMETER Scope
        Recommendation scope. Default: Shared.
 
    .PARAMETER ExpandUsage
        Include hourly usage data in the response.
 
    .PARAMETER ExpandAllRecommendationDetails
        Include all recommendation detail tiers in the response.
 
    .EXAMPLE
        Get-BenefitRecommendation -BillingAccountId "90846601" -LookBackPeriod Last30Days -Term P3Y -ExpandUsage -ExpandAllRecommendationDetails
    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        [string]$BillingAccountId,

        [ValidateSet('Last7Days', 'Last30Days', 'Last60Days')]
        [string]$LookBackPeriod = 'Last60Days',

        [ValidateSet('P1Y', 'P3Y')]
        [string]$Term = 'P3Y',

        [ValidateSet('Single', 'Shared')]
        [string]$Scope = 'Shared',

        [switch]$ExpandUsage,

        [switch]$ExpandAllRecommendationDetails
    )

    $path = "/providers/Microsoft.Billing/billingAccounts/$BillingAccountId/providers/Microsoft.CostManagement/benefitRecommendations"

    $filter = "properties/lookBackPeriod eq '$LookBackPeriod' AND properties/term eq '$Term' AND properties/scope eq '$Scope'"

    $expandParts = @()
    if ($ExpandUsage) { $expandParts += 'properties/usage' }
    if ($ExpandAllRecommendationDetails) { $expandParts += 'properties/allRecommendationDetails' }

    $queryString = "api-version=2025-03-01&`$filter=$filter"
    if ($expandParts.Count -gt 0) {
        $queryString += "&`$expand=$($expandParts -join ',')"
    }

    $fullPath = "${path}?${queryString}"
    Write-Verbose "Calling: $fullPath"

    $result = Invoke-AzRestMethod -Method GET -Path $fullPath -ErrorAction Stop

    if ($result.StatusCode -ne 200) {
        throw "API returned $($result.StatusCode): $($result.Content)"
    }

    $response = $result.Content | ConvertFrom-Json
    $count = ($response.value | Measure-Object).Count
    Write-Host "Retrieved $count benefit recommendation(s) for billing account $BillingAccountId" -ForegroundColor Green

    return $response.value
}

function Export-BenefitRecommendation {
    <#
    .SYNOPSIS
        Exports benefit recommendations to a JSON file for Kusto ingestion.
 
    .DESCRIPTION
        Takes benefit recommendation objects (from Get-BenefitRecommendation) and saves
        them as a JSON file ready for ingestion into the SavingsPlanRecommendations_raw table.
        Outputs the .set-or-append command to run in Azure Data Explorer.
 
    .PARAMETER Recommendations
        The recommendation array from Get-BenefitRecommendation. Accepts pipeline input.
 
    .PARAMETER OutputPath
        The output JSON file path. Default: BenefitRecommendations_<timestamp>.json in the current directory.
 
    .PARAMETER Table
        The target table name (used in the generated Kusto command). Default: SavingsPlanRecommendations_raw.
 
    .EXAMPLE
        Get-BenefitRecommendation -BillingAccountId "90846601" | Export-BenefitRecommendation
 
    .EXAMPLE
        $recs = Get-BenefitRecommendation -BillingAccountId "90846601"
        Export-BenefitRecommendation -Recommendations $recs -OutputPath "./recs.json"
    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory, ValueFromPipeline)]
        [object]$Recommendations,

        [string]$OutputPath,

        [string]$Table = 'SavingsPlanRecommendations_raw'
    )

    begin {
        $collected = [System.Collections.Generic.List[object]]::new()
    }

    process {
        if ($Recommendations -is [System.Collections.IEnumerable] -and $Recommendations -isnot [string]) {
            foreach ($item in $Recommendations) {
                $collected.Add($item)
            }
        }
        else {
            $collected.Add($Recommendations)
        }
    }

    end {
        if ($collected.Count -eq 0) {
            Write-Warning "No recommendations to export."
            return
        }

        $jsonArray = $collected | ConvertTo-Json -Depth 100 -Compress
        $command = ".set-or-append $Table <| print Recommendation=dynamic($jsonArray)"

        if ($OutputPath) {
            $command | Set-Content -Path $OutputPath -Encoding UTF8
            Write-Host "Exported Kusto command to $OutputPath" -ForegroundColor Green
        }

        Write-Host "Run this in Azure Data Explorer:" -ForegroundColor Yellow
        return $command
    }
}