Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Authentication/WorkloadIdentityFederation/Get-AzFederatedAssertion.ps1

<#
.SYNOPSIS
    Resolves a federated OIDC JWT assertion from one of the supported workload identity sources.
 
.DESCRIPTION
    Workload Identity Federation exchanges a short-lived token issued by an external identity
    provider for an Azure AD access token. This function only resolves *where that external
    token comes from* - the exchange itself happens in Get-AzWorkloadIdentityFederationToken.
 
    Three sources are supported:
      - File : read the raw JWT from a token file (the AKS/Kubernetes workload identity
                        pattern - the platform projects and periodically rotates this file).
      - GitHubActions : request a fresh ID token from the GitHub Actions OIDC endpoint using the
                        ACTIONS_ID_TOKEN_REQUEST_URL / ACTIONS_ID_TOKEN_REQUEST_TOKEN environment
                        variables GitHub injects into a workflow run (requires 'id-token: write'
                        permission on the job).
      - Manual : the caller already has a federated token (e.g. obtained via an Azure
                        DevOps Pipelines OIDC task) and supplies it directly - returned as-is.
 
.PARAMETER FederatedTokenFile
    Path to a file containing the raw federated JWT. Used with the 'File' source.
 
.PARAMETER GitHubActionsAudience
    The 'audience' value requested from the GitHub Actions OIDC endpoint. Defaults to
    'api://AzureADTokenExchange', the audience Azure AD expects for federated credentials.
 
.PARAMETER FederatedToken
    A federated JWT already obtained by the caller. Used with the 'Manual' source; returned as-is.
#>

Function Get-AzFederatedAssertion
{
    [CmdletBinding(DefaultParameterSetName = 'File')]
    param (
        [Parameter(Mandatory = $true, ParameterSetName = 'File')]
        [String]$FederatedTokenFile,

        [Parameter(Mandatory = $true, ParameterSetName = 'GitHubActions')]
        [Switch]$GitHubActions,

        [Parameter(ParameterSetName = 'GitHubActions')]
        [String]$GitHubActionsAudience = 'api://AzureADTokenExchange',

        [Parameter(Mandatory = $true, ParameterSetName = 'Manual')]
        [String]$FederatedToken
    )

    switch ($PSCmdlet.ParameterSetName)
    {
        'File'
        {
            Write-Verbose "[Get-AzFederatedAssertion] Reading federated token from file '$FederatedTokenFile'."

            if (-not (Test-Path -LiteralPath $FederatedTokenFile))
            {
                throw "[Get-AzFederatedAssertion] Federated token file not found at path '$FederatedTokenFile'."
            }

            $token = (Get-Content -LiteralPath $FederatedTokenFile -Raw).Trim()

            if ([String]::IsNullOrEmpty($token))
            {
                throw "[Get-AzFederatedAssertion] Federated token file '$FederatedTokenFile' is empty."
            }

            return $token
        }
        'GitHubActions'
        {
            Write-Verbose "[Get-AzFederatedAssertion] Requesting a federated token from the GitHub Actions OIDC endpoint."

            $requestUrl   = $env:ACTIONS_ID_TOKEN_REQUEST_URL
            $requestToken = $env:ACTIONS_ID_TOKEN_REQUEST_TOKEN

            if ([String]::IsNullOrEmpty($requestUrl) -or [String]::IsNullOrEmpty($requestToken))
            {
                throw "[Get-AzFederatedAssertion] ACTIONS_ID_TOKEN_REQUEST_URL / ACTIONS_ID_TOKEN_REQUEST_TOKEN are not set. Ensure the job has 'permissions: id-token: write' and is running inside GitHub Actions."
            }

            $separator = if ($requestUrl -match '\?') { '&' } else { '?' }
            $uri       = '{0}{1}audience={2}' -f $requestUrl, $separator, [Uri]::EscapeDataString($GitHubActionsAudience)

            try
            {
                $response = Invoke-RestMethod -Uri $uri -Method Get -Headers @{ Authorization = "Bearer $requestToken" }
            }
            catch
            {
                throw "[Get-AzFederatedAssertion] Failed to request a federated token from the GitHub Actions OIDC endpoint. Error: $_"
            }

            if ([String]::IsNullOrEmpty($response.value))
            {
                throw "[Get-AzFederatedAssertion] The GitHub Actions OIDC endpoint did not return a token value."
            }

            return $response.value
        }
        'Manual'
        {
            Write-Verbose "[Get-AzFederatedAssertion] Using the manually-supplied federated token."

            if ([String]::IsNullOrEmpty($FederatedToken))
            {
                throw "[Get-AzFederatedAssertion] The supplied federated token is empty."
            }

            return $FederatedToken
        }
    }

    throw "[Get-AzFederatedAssertion] Unhandled parameter set '$($PSCmdlet.ParameterSetName)'."
}