internal/functions/subscription/Update-PSEntraIDLicenseIdentifierCache.ps1

function Update-PSEntraIDLicenseIdentifierCache {
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions', '', Justification = 'Internal cache helper; refreshes a local CSV file without modifying remote tenant state.')]
    [CmdletBinding()]
    param (
        [Parameter()]
        [switch] $Force
    )

    [string] $cachePath = Get-PSFConfigValue -FullName ('{0}.LicenseIdentifiers.CachePath' -f $script:ModuleName) -Fallback (Get-PSEntraIDDefaultLicenseIdentifierCachePath)
    [string] $resolvedCachePath = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($cachePath)
    [string] $sourceUrl = Get-PSFConfigValue -FullName ('{0}.LicenseIdentifiers.SourceUrl' -f $script:ModuleName) -Fallback 'https://download.microsoft.com/download/e/3/e/e3e9faf2-f28b-490a-9ada-c6089a1fc5b0/Product%20names%20and%20service%20plan%20identifiers%20for%20licensing.csv'
    [int] $refreshIntervalDays = Get-PSFConfigValue -FullName ('{0}.LicenseIdentifiers.AutoUpdateIntervalDays' -f $script:ModuleName) -Fallback 30

    if (-not $Force -and (Test-Path -Path $resolvedCachePath -PathType Leaf)) {
        [datetime] $minimumWriteTime = [datetime]::UtcNow.AddDays(-1 * $refreshIntervalDays)
        if ((Get-Item -Path $resolvedCachePath).LastWriteTimeUtc -ge $minimumWriteTime) {
            return $resolvedCachePath
        }
    }

    [string] $cacheDirectory = Split-Path -Path $resolvedCachePath -Parent
    if (-not (Test-Path -Path $cacheDirectory)) {
        New-Item -Path $cacheDirectory -ItemType Directory -Force | Out-Null
    }

    [string] $downloadPath = [System.IO.Path]::ChangeExtension([System.IO.Path]::GetTempFileName(), '.csv')

    try {
        Invoke-WebRequest -Uri $sourceUrl -OutFile $downloadPath

        [object[]] $rows = @(Import-Csv -Path $downloadPath)
        if ($rows.Count -eq 0) {
            throw 'The licensing reference CSV did not contain any rows.'
        }

        [object[]] $licenseIdentifiers = @(foreach ($skuGroup in ($rows | Group-Object GUID, String_Id)) {
            [object[]] $groupRows = @($skuGroup.Group)
            [pscustomobject[]] $servicePlans = @(
                foreach ($servicePlanGroup in ($groupRows | Where-Object { -not [string]::IsNullOrWhiteSpace($_.Service_Plan_Id) } | Group-Object Service_Plan_Id, Service_Plan_Name)) {
                    [object] $servicePlanRow = $servicePlanGroup.Group |
                        Sort-Object @{ Expression = { [string]::IsNullOrWhiteSpace($_.Service_Plans_Included_Friendly_Names) } }, Service_Plans_Included_Friendly_Names |
                        Select-Object -First 1

                    [pscustomobject]@{
                        servicePlanId = $servicePlanRow.Service_Plan_Id
                        servicePlanName = $servicePlanRow.Service_Plan_Name
                        servicePlanFriendlyName = $servicePlanRow.Service_Plans_Included_Friendly_Names
                    }
                }
            ) | Sort-Object servicePlanName, servicePlanId

            [object] $skuRow = $groupRows |
                Sort-Object @{ Expression = { [string]::IsNullOrWhiteSpace($_.Product_Display_Name) } }, Product_Display_Name |
                Select-Object -First 1

            [pscustomobject]@{
                skuId = $skuRow.GUID
                skuPartNumber = $skuRow.String_Id
                skuFriendlyName = $skuRow.Product_Display_Name
                servicePlans = @($servicePlans)
            }
        }) | Sort-Object skuPartNumber, skuId

        [string] $json = $licenseIdentifiers | ConvertTo-Json -Depth 5
        [System.IO.File]::WriteAllText($resolvedCachePath, $json, [System.Text.UTF8Encoding]::new($false))

        return $resolvedCachePath
    }
    catch {
        Write-PSFMessage -Level Verbose -Message ('License identifier cache refresh failed: {0}' -f $_.Exception.Message)
        return $null
    }
    finally {
        if (Test-Path -Path $downloadPath) {
            Remove-Item -Path $downloadPath -Force
        }
    }
}