powershellazure-devopsazure-pipelines-release-pipelineazure-service-principal

How do I provide service principal's authorization for an API call in a DevOps pipeline


I have an Azure CLI task in a release pipeline that I would like to make an API call from back to the same project. The task is executing under a Service Principal, and it makes available the servicePrincipalId, servicePrincipalKey and tenantId to the script.

How can I use the Service Principal's credentials to authenticate a Invoke-RestMethod API request?


Solution

  • You can use the Service Principal's credentials to authenticate a Invoke-RestMethod API request with the servicePrincipalId, servicePrincipalKey and tenantId.

    First, you can check the document to learn about Use service principals & managed identities in Azure DevOps. To use the Service Principal's credentials, we should add the service principal to Azure DevOps organization.

    Here are the steps:

    1. Find and copy the App registration name by clicking the Manage App registration in the service connection. App registration name
    2. Add the App registration to your organization in the Users page. You should be a member of the Project Collection Administrators group. users page
    3. Now you can use the Service Principal's credentials to make an API call from back to the same project in the azure cli task.

    Here are the sample YAMLs:

    1. Use az account get-access-token command to get the token. This is also mentioned in this Q&A Can I use a service principal or managed identity with Azure CLI?.
    - task: AzureCLI@2
      inputs:
        azureSubscription: 'Test'
        scriptType: 'ps'
        scriptLocation: 'inlineScript'
        inlineScript: |
          Write-Host "Obtain access token for Service Connection identity..."
          $accessToken = az account get-access-token --resource 499b84ac-1321-427f-aa17-267ca6975798 --query "accessToken" --output tsv
          
          Write-Host "Use access token with Azure DevOps REST API to list projects in the organization..."
          $uri = "https://dev.azure.com/orgname/_apis/projects?api-version=7.1-preview.1"
          $headers = @{
              Accept = "application/json"
              Authorization = "Bearer $accessToken"
          }
          $response  =Invoke-RestMethod -Uri $uri -Headers $headers -Method Get 
          $response | ConvertTo-Json
    

    1

    1. Use the servicePrincipalId, servicePrincipalKey and tenantId to get the token.
    - task: AzureCLI@2
      displayName: Use servicePrincipalId, servicePrincipalKey and tenantId 
      inputs:
        azureSubscription: 'Test'
        scriptType: 'ps'
        addSpnToEnvironment: true
        scriptLocation: 'inlineScript'
        inlineScript: |
          $tenantId = $env:tenantId
          $clientId = $env:servicePrincipalId
          $clientSecret = $env:servicePrincipalKey
          $resource = "499b84ac-1321-427f-aa17-267ca6975798" #(the Azure DevOps resource's UUID is 499b84ac-1321-427f-aa17-267ca6975798) 
          
          $body = @{
              grant_type    = "client_credentials"
              client_id     = $clientId
              client_secret = $clientSecret
              resource      = $resource
          }
          
          # Get the token
          $tokenResponse = Invoke-RestMethod -Method Post -Uri "https://login.microsoftonline.com/$tenantId/oauth2/token"  -Body $body
          $token = $tokenResponse.access_token
          
          Write-Host "Use servicePrincipalId, servicePrincipalKey and tenantId  with Azure DevOps REST API to list project in the organization..."
          $uri = "https://dev.azure.com/orgname/_apis/projects?api-version=7.1"
          $headers = @{
              Accept = "application/json"
              Authorization = "Bearer $token"
          }
          $response  =Invoke-RestMethod -Uri $uri -Headers $headers -Method Get 
          $response | ConvertTo-Json
    

    2

    By the way, there is another easy way make an API call from back to the same project is to use the System.AccessToken. This way, we don't need to add the service principal to Azure DevOps organization.

    - task: AzureCLI@2
      displayName: Use System.AccessToken 
      inputs:
        azureSubscription: 'Test'
        scriptType: 'ps'
        scriptLocation: 'inlineScript'
        inlineScript: |
          Write-Host "Use System.AccessToken with Azure DevOps REST API to list project in the organization..."
          $uri = "https://dev.azure.com/orgname/_apis/projects?api-version=7.1"
          $headers = @{
              Accept = "application/json"
              Authorization = "Bearer $(System.AccessToken)"
          }
          $response  =Invoke-RestMethod -Uri $uri -Headers $headers -Method Get 
          $response | ConvertTo-Json
    

    3