0

Question: How do I troubleshoot a 401 (Unauthorized) error from Fabric REST API?

I have an App Registration that that uses a secret to authenticate with Fabric and I am able to issue REST API calls from DevOps for the purposes of automated deployments. So, the plumbing from DevOps through the App Registration out to Fabric works. Note: DevOps uses a Managed Identity in the service connection to retrieve the App Registration secret from the Azure Key Vault. I am golden here.

But, now I am attempting to implement Federated Credentials on the same App Registration for the purpose of eliminating the need for a secret (eliminate developer maintenance of renewing expired secrets and prevent leaking of the secret). This required a new service connection in DevOps.

In the build pipeline, I am able to use the new service connection (Note: it is a two-step process that requires getting both the Issuer and Subject Identifier from the App Registration's Federated Credential) to request a bearer token, which I received; However, I am getting stuck at calling the REST API when I present the bearer token - I get a 401 (Unauthorized) error. I have searched high and low on the internet to figure out how to resolve this, but I have come up empty handed.

enter image description here

Documentation online detailing the configuration and testing is sparse. Admittedly, there is more documentation on the former than the latter. But, the latter is the kicker - Federated Credentials doesn't work for me when the Fabric REST API rejects my bearer token.

Here is the PowerShell script that I using in the build pipeline. How do I fix this? Can the same App Registration be used for authentication either with a secret or federated credentials. Or, am I calling the pipeline incorrectly

write-host "Requesting bearer token."
$bearerToken  = (Get-AzAccessToken -ResourceUrl "https://api.fabric.microsoft.com").Token | ConvertFrom-SecureString
        $headers = @{
              "Authorization" = "Bearer $bearerToken"
                "Content-Type" = "application/json"
              }
write-host "The bearer token is: $bearerToken"

Write-Host "Calling Fabric API..."
$headers = @{ Authorization = "Bearer $bearerToken" }
$scope = 'https://analysis.windows.net/powerbi/api/.default'
$fabricApiUrl = 'https://api.fabric.microsoft.com/v1/workspaces'
$response = Invoke-RestMethod -Uri $fabricApiUrl -Headers $headers -Method Get
Write-Host "$response"
2
  • "404 (Unauthorized)" is a bit weird. In the HTTP spec, 404 is defined as "not found". Commented Oct 1 at 14:32
  • @JesperJuhl That was a typo. I have updated the question. My apologies for the confusion. Commented Oct 1 at 15:23

1 Answer 1

1

I figured it out. My configuration within Azure was correct. The problem was with the Build Pipeline's Powershell script. To get the required Fabric REST API token, I needed to:

  1. Use an Azure CLI task and inject the Service Principal's (SP) details into the script with addSpnToEnvironment: true and then assign the Service Principal's ID, Tenant ID, and ID Token to a pipeline variable.

  2. Retrieve the Fabric REST API JWT Bearer token from https://login.microsoftonline.com/$tenantId/oauth2/v2.0/token.

  3. Make REST API calls to Fabric.

A few things that I found challenging is that I was trying to use

Get-AzAccessToken -ResourceUrl "https://api.fabric.microsoft.com").Token | ConvertFrom-SecureString

to retrieve the Fabric REST API token. But, that token was invalid and nowhere near the form of the token that was being received when using the SP's Client Secret. In fact, none of the AZ CLI or Powershell Azure commands gave me a working token - I had to use the Microsoft OATH2 REST API to get the token. Additionally, the SP's Client ID is protected in this mode because that is how the Federated Credential establishes a trust relationship between DevOps and Azure, and thus treats it as a secret. So, I spent an inordinate amount of time just trying to get the Client ID value, let alone a token value, to the log for the purpose of trying to isolate the problem as either a configuration issue with the Federated Credential or a code issue in Powershell. As a consequence, there is no easy way that I could find to retrieve the Federated Credential(s) as a means for testing whether the configuration of said Federated Credential was correct of if there was a problem with the pipeline code; and so that left me stuck trying to troubleshoot two moving targets simultaneously. Additionally, none of the online resources that I browsed through connected all the dots (see code below) on how to get the correct access token for the Fabric REST API. It would have been nice if Microsoft included this in their documentation somewhere.

As a final point, a similar answer on SO (see References below) provides a good walk-through on how to setup a Federated Credential between a Service Principal and DevOps, but it does not explain how to properly get the Fabric REST API token (again, I get a different token using either the AZ CLI or Powershell commands to query Azure directly as opposed to going to Microsoft's OATH2 REST API) that is required to call the Fabric REST API.

I hope this help others.

# Demo pipeline for authenticating to the target Service Principal using a Federated credential between DevOps and the Service Principal. Once authenticated, this pipeline will submit a Fabric REST API request to view all workspace(s).
# Note: The only thing that you need to provide is the azureSubscription name, which is the name of the DevOps Service Connection that has a Federated Credential setup to the Service Principal.

trigger:
  branches:
    include:
      - CI-Federated-Credential-Authentication-Example

pool:
  vmImage: 'windows-latest'

variables:
  FABRIC_SCOPE: 'https://api.fabric.microsoft.com/.default'
  FABRIC_API_URL: 'https://api.fabric.microsoft.com/v1/workspaces'

steps:
- task: AzureCLI@2
  displayName: 'Azure CLI: Authenticate with Fed Cred and list Fabric REST API Workspaces'
  inputs:
    azureSubscription: 'test'
    scriptType: 'pscore'
    scriptLocation: 'inlineScript'
    addSpnToEnvironment: true
    inlineScript: |
      Write-Host "Authenticating to Microsoft Fabric using federated credentials..."

      $clientId = $env:servicePrincipalId
      $tenantId = $env:tenantId
      $idToken = $env:idToken
      $scope = $env:FABRIC_SCOPE

      $body = @{
        client_id              = $clientId
        scope                  = $scope
        grant_type             = "client_credentials"
        client_assertion       = $idToken
        client_assertion_type  = "urn:ietf:params:oauth:client-assertion-type:jwt-bearer"
      }

      $response = Invoke-RestMethod -Method Post `
        -Uri "https://login.microsoftonline.com/$tenantId/oauth2/v2.0/token" `
        -ContentType "application/x-www-form-urlencoded" `
        -Body $body

      if (-not $response.access_token) {
        Write-Error "Failed to retrieve access token."
        exit 1
      }

      $accessToken = $response.access_token
      Write-Host "Access token retrieved successfully."

      # Call Microsoft Fabric API to list workspaces
      $fabricApiUrl = $env:FABRIC_API_URL
      $headers = @{ Authorization = "Bearer $accessToken" }

      $workspaceResponse = Invoke-RestMethod -Uri $fabricApiUrl -Method Get -Headers $headers

      Write-Host "Workspaces retrieved:"
      $workspaceResponse.value | ForEach-Object {
        Write-Host " - $($_.displayName) (ID: $($_.id))"
      }

Reference(s):

https://stackoverflow.com/a/78297452/4630376

https://learn.microsoft.com/en-us/azure/devops/pipelines/tasks/reference/azure-cli-v2?view=azure-pipelines

https://github.com/Microsoft/azure-pipelines-tasks/issues/9554

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.