internal/functions/Get-BapContext.ps1

<#
    Central identity lookup for the active auth provider.
 
    Get-BapContext collapses the provider branching (AzAccount via Az.Accounts,
    AzCli via the Azure CLI) behind a single call that returns the current
    sign-in as a full object, so cmdlets no longer inline Get-AzContext /
    az account show handling.
#>


function Get-BapContext {
    <#
        .SYNOPSIS
            Gets the current Azure sign-in as a full context object.
 
        .DESCRIPTION
            Returns the current Azure context for the active auth provider
            ('d365bap.tools.auth.provider', default 'AzAccount') as an object with
            TenantId, TenantName, Upn and UserId properties.
 
            TenantId and Upn resolve from the plain sign-in state on both providers.
            TenantName and UserId are best-effort: TenantName falls back to an empty
            string when tenant metadata cannot be read, and UserId needs Microsoft
            Graph access in AzCli mode ('az ad signed-in-user show'), so it stays
            empty when Graph is unavailable. Pass -RequireUserId when the caller
            cannot proceed without the Entra object id.
 
            The call fails fast with an actionable message only when no usable
            sign-in exists at all (i.e. the user is logged out).
 
        .PARAMETER RequireUserId
            Fail fast with a Graph-access hint when the Entra user id cannot be
            determined, instead of returning an object with an empty UserId.
 
        .PARAMETER AllowEmpty
            Return $null instead of stopping when no usable sign-in exists. For
            best-effort lookups (e.g. cache keys) that handle a missing sign-in
            themselves.
 
        .EXAMPLE
            PS C:\> $tenantId = (Get-BapContext).TenantId
 
            Returns the current tenant id or stops with an actionable message when logged out.
 
        .NOTES
            Author: Motz Jensen (@Splaxi)
    #>

    [CmdletBinding()]
    [OutputType([PSCustomObject])]
    param (
        [switch] $RequireUserId,

        [switch] $AllowEmpty
    )

    $authProvider = Get-PSFConfigValue -FullName 'd365bap.tools.auth.provider' -Fallback 'AzAccount'

    $tenantId = $null
    $tenantName = ''
    $upn = $null
    $userId = ''

    if ($authProvider -eq 'AzCli') {
        try {
            $azShow = az account show --query '{tenant:tenantId, user:user.name}' -o json 2>$null
            if ($LASTEXITCODE -eq 0 -and -not [string]::IsNullOrWhiteSpace("$azShow")) {
                $azContext = "$azShow" | ConvertFrom-Json -ErrorAction Stop
                if ($null -ne $azContext -and -not [string]::IsNullOrWhiteSpace("$($azContext.tenant)")) {
                    $tenantId = "$($azContext.tenant)".Trim()
                }
                if ($null -ne $azContext -and -not [string]::IsNullOrWhiteSpace("$($azContext.user)")) {
                    $upn = "$($azContext.user)".Trim()
                }
            }
        }
        catch {
            Write-PSFMessage -Level Verbose -Message "az account show failed with: $($_.Exception.Message)"
        }

        if (-not [string]::IsNullOrWhiteSpace("$tenantId")) {
            try {
                $rawNames = az account tenant list --query "[?tenantId=='$tenantId'].displayName" -o tsv 2>$null
                if ($LASTEXITCODE -eq 0) {
                    $firstName = @($rawNames | Where-Object { -not [string]::IsNullOrWhiteSpace("$_") }) | Select-Object -First 1
                    if ($null -ne $firstName) {
                        $tenantName = "$firstName".Trim()
                    }
                }
            }
            catch {
                Write-PSFMessage -Level Verbose -Message "az account tenant list failed with: $($_.Exception.Message)"
            }

            try {
                $azUserId = az ad signed-in-user show --query id -o tsv 2>$null
                if ($LASTEXITCODE -eq 0 -and -not [string]::IsNullOrWhiteSpace("$azUserId")) {
                    $userId = "$azUserId".Trim()
                }
            }
            catch {
                Write-PSFMessage -Level Verbose -Message "az ad signed-in-user show failed with: $($_.Exception.Message)"
            }
        }
    }
    else {
        try {
            $azContext = Get-AzContext -ErrorAction Stop
            if ($null -ne $azContext) {
                if ($null -ne $azContext.Tenant -and -not [string]::IsNullOrWhiteSpace("$($azContext.Tenant.Id)")) {
                    $tenantId = "$($azContext.Tenant.Id)".Trim()
                }
                if ($null -ne $azContext.Account -and -not [string]::IsNullOrWhiteSpace("$($azContext.Account.Id)")) {
                    $upn = "$($azContext.Account.Id)".Trim()
                }
                if ($null -ne $azContext.Account -and $null -ne $azContext.Account.ExtendedProperties) {
                    $extObj = $azContext.Account.ExtendedProperties
                    if (-not [string]::IsNullOrWhiteSpace("$($extObj.HomeAccountId)")) {
                        $userId = "$($extObj.HomeAccountId)".Replace(".$($extObj.Tenants)", "")
                    }
                }
            }
        }
        catch {
            Write-PSFMessage -Level Verbose -Message "Get-AzContext failed with: $($_.Exception.Message)"
        }

        if (-not [string]::IsNullOrWhiteSpace("$tenantId")) {
            try {
                $azTenant = Get-AzTenant -ErrorAction Stop | Where-Object { $_.Id -eq $tenantId } | Select-Object -First 1
                if ($null -ne $azTenant -and -not [string]::IsNullOrWhiteSpace("$($azTenant.Name)")) {
                    $tenantName = "$($azTenant.Name)".Trim()
                }
            }
            catch {
                Write-PSFMessage -Level Verbose -Message "Get-AzTenant failed with: $($_.Exception.Message)"
            }
        }
    }

    if ([string]::IsNullOrWhiteSpace("$tenantId") -or [string]::IsNullOrWhiteSpace("$upn")) {
        if ($AllowEmpty) { return $null }
        $messageString = "Unable to determine the current Azure sign-in. Please run <c='em'>Connect-AzAccount</c> or <c='em'>az login</c> and try again."
        Write-PSFMessage -Level Important -Message $messageString
        Stop-PSFFunction -Message "Stopping because the current Azure sign-in could not be determined." -Exception $([System.Exception]::new($($messageString -replace '<[^>]+>', ''))) -StepsUpward 1
        return
    }

    if ($RequireUserId -and [string]::IsNullOrWhiteSpace("$userId")) {
        $messageString = "Unable to determine the current Entra user id. Please run <c='em'>Connect-AzAccount</c>, or <c='em'>az login</c> with a user account that has Microsoft Graph access, and try again."
        Write-PSFMessage -Level Important -Message $messageString
        Stop-PSFFunction -Message "Stopping because the current Entra user id could not be determined." -Exception $([System.Exception]::new($($messageString -replace '<[^>]+>', ''))) -StepsUpward 1
        return
    }

    $context = [PSCustomObject]@{
        TenantId   = "$tenantId"
        TenantName = "$tenantName"
        Upn        = "$upn"
        UserId     = "$userId"
    }
    $context.PSObject.TypeNames.Insert(0, 'D365Bap.Tools.CurrentContext')
    return $context
}