src/Connect-CciGet.ps1

function Connect-CciGet {
<#
.SYNOPSIS
    Register configured CCI feeds as PSResource repositories and prepare credentials.
.DESCRIPTION
    For each enabled feed in Get-CciGetConfig, Connect-CciGet:
      - Ensures the Azure Artifacts Credential Provider is installed (one-time
        bootstrap on Windows; uses the Microsoft installer at
        https://github.com/microsoft/artifacts-credprovider).
      - Calls Register-PSResourceRepository for the feed (idempotent).
    After Connect-CciGet succeeds, Find-CciModule / Install-CciModule will
    transparently use the user's Entra identity (or, in CI, a workload identity)
    to authenticate to the feed. No PATs are required.
.PARAMETER Tenant
    Optional. Limit registration to the named feed only.
.PARAMETER SkipCredentialProvider
    Skip the credential-provider bootstrap (use when you have managed it
    centrally or are running in CI with a different auth strategy).
#>

    [CmdletBinding()]
    param(
        [string]$Tenant,
        [switch]$SkipCredentialProvider
    )

    if (-not (Get-Module -ListAvailable -Name Microsoft.PowerShell.PSResourceGet)) {
        throw "cciget: Microsoft.PowerShell.PSResourceGet is required but not installed. Run: Install-Module Microsoft.PowerShell.PSResourceGet -Scope CurrentUser"
    }
    Import-Module Microsoft.PowerShell.PSResourceGet -ErrorAction Stop

    if (-not $SkipCredentialProvider) {
        $pluginRoot = Join-Path $env:USERPROFILE '.nuget\plugins\netcore'
        if (-not (Test-Path $pluginRoot) -or -not (Get-ChildItem $pluginRoot -Filter 'CredentialProvider.Microsoft.dll' -Recurse -ErrorAction SilentlyContinue)) {
            Write-Host "cciget: installing Azure Artifacts Credential Provider..."
            try {
                $script = (New-Object System.Net.WebClient).DownloadString('https://aka.ms/install-artifacts-credprovider.ps1')
                Invoke-Expression $script
            } catch {
                Write-Warning "cciget: credential provider install failed: $_. You may need to install manually from https://github.com/microsoft/artifacts-credprovider."
            }
        }
    }

    $feeds = _Resolve-CciGetFeed -Tenant $Tenant
    $authFailed = @()

    # Configure credential provider env vars for the session.
    # These persist after Connect-CciGet returns so Find-CciModule benefits too.
    $env:NUGET_CREDENTIALPROVIDER_MSAL_ALLOW_DEVICECODEFLOW = 'true'
    $env:NUGET_CREDENTIALPROVIDER_MSAL_ALLOWBROKER = 'true'

    # If an Azure CLI session is active, pre-seed feed credentials so the
    # credential provider doesn't need to prompt interactively. This allows
    # non-interactive scenarios (remoting, CI, WAM-incompatible shells).
    $azdoToken = $null
    if (Get-Command az -ErrorAction SilentlyContinue) {
        try {
            $azdoToken = az account get-access-token --resource '499b84ac-1321-427f-aa17-267ca6975798' --query accessToken -o tsv 2>$null
        } catch { }
    }
    if ($azdoToken) {
        $endpoints = @{ endpointCredentials = @($feeds | ForEach-Object {
            @{ endpoint = $_.url; username = 'VssSessionToken'; password = $azdoToken }
        }) }
        $env:VSS_NUGET_EXTERNAL_FEED_ENDPOINTS = $endpoints | ConvertTo-Json -Compress -Depth 3
        Write-Verbose "cciget: seeded feed credentials from Azure CLI session."
    }

    foreach ($feed in $feeds) {
        $repoName = _Get-CciGetRepositoryName -FeedName $feed.name

        # Set tenant-specific authority for this feed.
        if ($feed.tenantId) {
            $env:NUGET_CREDENTIALPROVIDER_MSAL_AUTHORITY = "https://login.microsoftonline.com/$($feed.tenantId)"
        }

        $existing = Get-PSResourceRepository -Name $repoName -ErrorAction SilentlyContinue
        if ($existing) {
            if ($existing.Uri -ne $feed.url) {
                Set-PSResourceRepository -Name $repoName -Uri $feed.url -Trusted
                Write-Verbose "cciget: updated $repoName URL."
            }
        } else {
            Register-PSResourceRepository -Name $repoName -Uri $feed.url -Trusted
            Write-Host "cciget: registered repository '$repoName' -> $($feed.url)"
        }

        # Verify auth by probing the feed. This triggers the credential provider
        # now (at connect time) so auth issues surface here, not in Find-CciModule.
        Write-Host "cciget: authenticating to '$repoName'..."
        $probeOk = $false
        try {
            # Use a wildcard probe — this hits the V2 search endpoint which
            # actually requires authentication, unlike specific-name lookups
            # which may return empty 200s without auth.
            $null = Find-PSResource -Name '*' -Repository $repoName -ErrorAction Stop
            # Got results — auth works and the feed has packages.
            $probeOk = $true
        } catch {
            $msg = $_.Exception.Message
            if ($msg -match 'No match was found|could not be found in repository') {
                # Auth succeeded — feed is simply empty.
                $probeOk = $true
            } elseif ($msg -match '\[Warning\].*CredentialProvider' -and $msg -notmatch '401|Unauthorized|forbidden') {
                Write-Verbose "cciget: credential provider warning (non-fatal): $msg"
                $probeOk = $true
            } else {
                Write-Warning "cciget: authentication failed for '$repoName'."
                Write-Warning " Error: $msg"
                if (-not $azdoToken) {
                    Write-Warning " Hint: install Azure CLI and run 'az login' first, then retry Connect-CciGet."
                }
                $authFailed += $repoName
            }
        }

        if ($probeOk) {
            Write-Host "cciget: '$repoName' authenticated and ready."
        }
    }

    if ($authFailed.Count -gt 0) {
        Write-Warning "cciget: $($authFailed.Count) feed(s) failed authentication: $($authFailed -join ', ')"
        Write-Warning "cciget: You may need to run Connect-CciGet again. If prompted for a device code, complete the sign-in in your browser."
    }

    # Return a clean summary instead of the wide default table.
    Get-PSResourceRepository -Name (_Get-CciGetRepositoryName -FeedName '*') |
        Select-Object Name, Uri, Trusted
}