Private/Get-EntraAccessTokenFromCli.ps1

function Get-EntraAccessTokenFromCli {
    <#
    .SYNOPSIS
    An Azure DevOps access token from the signed-in Azure CLI, or $null.
 
    .DESCRIPTION
    Az PowerShell and the Azure CLI keep SEPARATE credential stores. Customer workspaces
    document 'az login' everywhere - runbooks, manifest.yaml, the CI setup guide - so an
    operator who has done exactly what they were told still had no context as far as
    Get-AzContext was concerned, and was prompted to sign in again. This reads the store
    they actually used.
 
    Returns a @{ Token; ExpiresOn } hashtable shaped for the caller's token cache, or
    $null when the CLI is absent, not signed in, has no account in the tenant, or cannot
    mint a token for any other reason. Never throws: it is one source among several, and
    an unavailable source is not an error.
 
    .PARAMETER TenantId
    The Entra tenant the token must come from.
 
    .PARAMETER Resource
    The resource (application ID) to request the token for.
 
    .PARAMETER AccountId
    Optional. The user principal name the organisation expects. When the CLI is signed in
    as somebody else, this returns $null rather than a token for the wrong identity - the
    caller then signs in properly.
    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory)] [string]$TenantId,
        [Parameter(Mandatory)] [string]$Resource,
        [string]$AccountId
    )

    $az = Get-Command az -ErrorAction SilentlyContinue
    if (-not $az) { return $null }

    # Native command failures must stay inspectable rather than terminating, since 'not
    # signed in' and 'no account in this tenant' are ordinary outcomes here.
    $PSNativeCommandUseErrorActionPreference = $false
    $ErrorActionPreference = 'Continue'

    try {
        if ($AccountId) {
            # Only trust the CLI when it holds the identity this organisation asked for.
            $signedIn = & az account show --query 'user.name' -o tsv 2>$null
            if ($LASTEXITCODE -ne 0 -or -not $signedIn) { return $null }
            if (([string]$signedIn).Trim() -ine $AccountId) {
                Write-Verbose "Azure CLI is signed in as '$signedIn', not '$AccountId'; skipping the CLI."
                return $null
            }
        }

        $json = & az account get-access-token --resource $Resource --tenant $TenantId -o json 2>$null
        if ($LASTEXITCODE -ne 0 -or -not $json) { return $null }

        $parsed = ($json -join '') | ConvertFrom-Json
        if (-not $parsed.accessToken) { return $null }

        $expiresOn = if ($parsed.expiresOn) {
            try { [datetime]::Parse($parsed.expiresOn) } catch { (Get-Date).AddMinutes(45) }
        }
        else { (Get-Date).AddMinutes(45) }

        Write-Verbose "Acquired an Azure DevOps token from the Azure CLI for tenant $TenantId."
        return @{ Token = [string]$parsed.accessToken; ExpiresOn = $expiresOn }
    }
    catch {
        Write-Verbose "Azure CLI token acquisition failed: $($_.Exception.Message)"
        return $null
    }
}