Reputation: 2947
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?
Upvotes: 0
Views: 116
Reputation: 2947
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).
Upvotes: 0
Reputation: 5432
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:
Here are the sample YAMLs:
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
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
Upvotes: 0
Reputation: 1451
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
Upvotes: 0