Modules/businessdev.ALbuild.Containers/Public/Find-BcArtifactUrl.ps1

function Find-BcArtifactUrl {
    <#
    .SYNOPSIS
        Finds Business Central artifact URLs from the public artifact indexes.
 
    .DESCRIPTION
        Queries the Business Central artifact storage indexes
        (https://bcartifacts-exdbf9fwegejdqak.b02.azurefd.net/{type}/indexes) and returns matching
        artifact URLs of the form https://.../{type}/{version}/{country}. This is the cross-platform basis
        for creating containers and for resolving the platform/application baseline.
 
    .PARAMETER Type
        OnPrem or Sandbox (default Sandbox).
 
    .PARAMETER Country
        Localisation (e.g. w1, de, us). Empty queries all countries.
 
    .PARAMETER Version
        Version or version prefix to match (e.g. '25', '25.1', '25.1.12345.0').
 
    .PARAMETER Select
        Selection strategy: Latest (default), First, All, Closest, SecondToLastMajor, Current,
        NextMinor, NextMajor.
 
    .PARAMETER StorageAccount
        Override the storage account (advanced; default bcartifacts).
 
    .PARAMETER AcceptInsiderEula
        Required to query insider artifacts (NextMinor/NextMajor), confirming acceptance of the
        Business Central Insider EULA (https://go.microsoft.com/fwlink/?linkid=2245051).
 
    .PARAMETER DoNotCheckPlatform
        Skip confirming that a matching platform build exists in the index.
 
    .EXAMPLE
        Find-BcArtifactUrl -Type Sandbox -Country w1 -Select Latest
 
    .EXAMPLE
        Find-BcArtifactUrl -Type OnPrem -Country de -Version 25.1
 
    .OUTPUTS
        System.String (one or more artifact URLs).
    #>

    [CmdletBinding()]
    [OutputType([string])]
    param(
        [ValidateSet('OnPrem', 'Sandbox')]
        [string] $Type = 'Sandbox',

        [string] $Country = '',

        [string] $Version = '',

        [ValidateSet('Latest', 'First', 'All', 'Closest', 'SecondToLastMajor', 'Current', 'NextMinor', 'NextMajor')]
        [string] $Select = 'Latest',

        [string] $StorageAccount = '',

        [switch] $AcceptInsiderEula,

        [switch] $DoNotCheckPlatform
    )

    # Known on-premises build replacements (historical, mirrors Microsoft's published guidance).
    if ($Type -eq 'OnPrem') {
        switch -Wildcard ($Version) {
            '18.9*'  { Write-ALbuildLog -Level Warning 'On-premises 18.9 was replaced by 18.10.35134.0; using that.'; $Version = '18.10.35134.0' }
            '17.14*' { Write-ALbuildLog -Level Warning 'On-premises 17.14 was replaced by 17.15.35135.0; using that.'; $Version = '17.15.35135.0' }
            '16.18*' { Write-ALbuildLog -Level Warning 'On-premises 16.18 was replaced by 16.19.35126.0; using that.'; $Version = '16.19.35126.0' }
        }
    }

    if ($Select -eq 'Current') {
        if ($StorageAccount -or $Type -eq 'OnPrem' -or $Version) {
            throw "You cannot specify StorageAccount, Type=OnPrem or Version when Select is 'Current'."
        }
        return Find-BcArtifactUrl -Type Sandbox -Country $Country -Select Latest -DoNotCheckPlatform:$DoNotCheckPlatform
    }

    if ($Select -eq 'NextMinor' -or $Select -eq 'NextMajor') {
        if ($StorageAccount -or $Type -eq 'OnPrem' -or $Version) {
            throw "You cannot specify StorageAccount, Type=OnPrem or Version when Select is '$Select'."
        }
        if (-not $AcceptInsiderEula) {
            throw "Querying insider artifacts ($Select) requires -AcceptInsiderEula (https://go.microsoft.com/fwlink/?linkid=2245051)."
        }

        $currentUrl = Find-BcArtifactUrl -Type Sandbox -Country 'base' -Select Latest -DoNotCheckPlatform:$DoNotCheckPlatform
        $currentVersion = [version]($currentUrl.Split('/')[4])
        $nextMinorPrefix = "$($currentVersion.Major).$($currentVersion.Minor + 1)."
        $nextMajorPrefix = "$($currentVersion.Major + 1).0."
        if ($currentVersion.Minor -ge 5) { $nextMinorPrefix = $nextMajorPrefix }

        $resolvedCountry = if ($Country) { $Country } else { 'w1' }
        $storage = Resolve-BcArtifactStorage -Type Sandbox -Insider
        if (-not $storage.IsInsider) { throw 'Insider storage could not be resolved.' }
        $insiders = Get-BcArtifactCandidate -Storage $storage -Country $resolvedCountry -DoNotCheckPlatform:$DoNotCheckPlatform

        $wantedPrefix = if ($Select -eq 'NextMinor') { $nextMinorPrefix } else { $nextMajorPrefix }
        $match = $insiders | Where-Object { ($_ -split '/', 2)[0].StartsWith($wantedPrefix) } | Select-Object -Last 1
        if (-not $match) { return }
        $version = ($match -split '/', 2)[0]
        return "$($storage.BaseUrl)$version/$resolvedCountry"
    }

    $storage = Resolve-BcArtifactStorage -Type $Type -StorageAccount $StorageAccount
    if ($storage.IsInsider -and -not $AcceptInsiderEula) {
        throw "Querying insider artifacts requires -AcceptInsiderEula (https://go.microsoft.com/fwlink/?linkid=2245051)."
    }

    # Compute the version prefix used to filter the index.
    $versionPrefix = ''
    $closestTo = ''
    if ($Select -eq 'Closest') {
        $parsed = [version]'0.0.0.0'
        if (($Version.ToCharArray() -eq '.').Count -ne 3 -or -not [version]::TryParse($Version, [ref] $parsed)) {
            throw "Version must be a full version (1.2.3.4) when Select is 'Closest'."
        }
        $versionPrefix = "$($parsed.Major).$($parsed.Minor)."
        $closestTo = $parsed.ToString()
    }
    elseif ($Select -eq 'SecondToLastMajor') {
        if ($Version) { throw "You cannot specify a Version when Select is 'SecondToLastMajor'." }
    }
    elseif (-not [string]::IsNullOrEmpty($Version)) {
        $versionPrefix = if (($Version.ToCharArray() -eq '.').Count -lt 3) { "$($Version.TrimEnd('.'))." } else { $Version }
    }

    $candidates = Get-BcArtifactCandidate -Storage $storage -Country $Country -VersionPrefix $versionPrefix -DoNotCheckPlatform:$DoNotCheckPlatform

    if ($Select -eq 'SecondToLastMajor') {
        $latest = $candidates | Select-Object -Last 1
        if (-not $latest) { return }
        $latestMajor = ([version](($latest -split '/', 2)[0])).Major
        $chosen = $candidates | Where-Object { ([version](($_ -split '/', 2)[0])).Major -ne $latestMajor } | Select-Object -Last 1
    }
    else {
        $chosen = Select-BcArtifactVersion -Candidates $candidates -Select $Select -ClosestToVersion $closestTo
    }

    foreach ($entry in @($chosen)) {
        if ($entry) {
            $parts = $entry -split '/', 2
            "$($storage.BaseUrl)$($parts[0])/$($parts[1])"
        }
    }
}