admin管理员组

文章数量:1122791

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?

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?

Share Improve this question edited Nov 21, 2024 at 23:18 Rui Jarimba 17.4k11 gold badges64 silver badges97 bronze badges asked Nov 21, 2024 at 22:42 end-userend-user 2,9476 gold badges32 silver badges58 bronze badges 2
  • This question is similar to: How to use Service Principal Authentication (Bearer Token) in Powershell for DevOps without being asked to sign in?. If you believe it’s different, please edit the question, make it clear how it’s different and/or how the answers on that question are not helpful for your problem. – akseli Commented Nov 21, 2024 at 22:52
  • @akseli - agreed, there are similar elements. I think the difference with mine is, according to the answers below, I cannot use the three pieces directly. I have to get an access token to make the call. – end-user Commented Nov 22, 2024 at 12:12
Add a comment  | 

3 Answers 3

Reset to default 0

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.
  2. Add the App registration to your organization in the Users page. You should be a member of the Project Collection Administrators group.
  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. 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

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

Considering that you use azure cli task and service principal, I guess your service principal is used to create service connection. Am I right?

If my guess is correct [if not, please ignore my answer -_-], you don't need to get the actual value of the service principal, but directly get the token, and then use the token to send the Invoke-RestMethod request. az account get-access-token

Below is a sample to illustrate the case (get azure subscription):

pool:
  vmImage: windows-latest

steps:

- task: AzureCLI@2
  displayName: 'Azure CLI'
  inputs:
    scriptType: ps
    scriptLocation: inlineScript
    azureSubscription: 'DevOpsSub1Connection-Test'
    inlineScript: |
      $subscriptionId = 'xxxxxxx-xxxx-xxxx-xxxx-xxxxxx'
      
      $token = $(az account get-access-token --resource-type arm --query accessToken --output tsv)

      $headers = @{
          "Content-Type" = "application/json"
          "Authorization" = "Bearer " + $token
      }

      $uri = "https://management.azure.com/subscriptions/{0}?api-version=2022-12-01" -f $subscriptionId

      $response = Invoke-RestMethod -Method Get -Uri $uri -Headers $headers

      $response | ConvertTo-Json -Depth 10

Ok, I was able to use @Miao Tian-MSFT's answer to get the token I needed.

$accessToken = az account get-access-token --resource 499b84ac-1321-427f-aa17-267ca6975798 --query "accessToken" --output tsv

However, I also later discovered the piece I was missing - on the Agent job, be sure to check the "Allow scripts to access the OAuth token" box. That's what provides the $System.AccessToken to the scripts (making the above call unnecessary).

本文标签: