How to add team members in Azure Devops via API? Also Groups API does not work - azure

I am new to Azure Devops and currently migrating to it. I want to add team members for my azure project via REST API. I referred the following documentation, but there is no mention of it. 'Teams' API has no functionality to add Members to it, rather only to create a Team with the Team Name of your choice.
https://learn.microsoft.com/en-us/rest/api/azure/devops/?view=azure-devops-rest-5.1
I encountered another problem in the Group Entitlements API:
https://learn.microsoft.com/en-us/rest/api/azure/devops/memberentitlementmanagement/group%20entitlements/list?view=azure-devops-rest-5.1
I am unable to hit this particular URL: https://vsaex.dev.azure.com.
In the other API examples, they have used only https://dev.azure.com which works perfectly fine for me. I do not understand what the vsaex stands for. Adding 'vsaex' or ignoring it did not work either. I could not find any documentation regarding this.
Same problem arises for vsaex.dev.azure.com for Users API.
Solutions to any of these would be helpful. Thanks in advance :)

I recently write a PowerShell Script to solve your first problem, but it is only tested on a local azure devops server.
class REST {
#PROPERTIES
[string]$ContentType = "application/json;charset=utf-8"
[string]$PAT
[System.Collections.IDictionary]$Headers
[string]$Url
[string]$Collection
[string]$_Project
#STATIC PROPERTIES
static [int]$Timeout = 30
#CONSTRUCTOR
REST([string]$PAT, [string]$Url, [string]$Collection, [string]$Project) { $this.Init($PAT, $Url, $Collection, $Project) }
REST([string]$PAT, [string]$Url, [string]$Collection) { $this.Init($PAT, $Url, $Collection, $null) }
REST([string]$PAT, [string]$Url) { $this.Init($PAT, $Url, $null, $null) }
REST([string]$PAT) { $this.Init($PAT, $null, $null, $null) }
#INITIALIZE
[void]Init([string]$PAT, [string]$Url, [string]$Collection, [string]$Project) {
$this.PAT = $PAT
$this.Url = $Url
$this.Collection = $Collection
$this._Project = $Project
$this.Headers = $(Headers -PAT $PAT)
}
#GET
[PSCustomObject]Get([string]$Uri) { return Invoke-RestMethod -Uri $Uri -Method GET -ContentType $this.ContentType -Headers $this.Headers -TimeoutSec $([REST]::Timeout) -Verbose }
#PUT
[PSCustomObject]Put([string]$Uri, $Body) { return Invoke-RestMethod -Uri $Uri -Method PUT -ContentType $this.ContentType -Headers $this.Headers -Body $Body -TimeoutSec $([REST]::Timeout) -Verbose }
#POST
[PSCustomObject]Post([string]$Uri, $Body) { return Invoke-RestMethod -Uri $Uri -Method POST -ContentType $this.ContentType -Headers $this.Headers -Body $Body -TimeoutSec $([REST]::Timeout) -Verbose }
#DELETE
[PSCustomObject]Delete([string]$Uri) { return Invoke-RestMethod -Uri $Uri -Method DELETE -ContentType $this.ContentType -Headers $this.Headers -TimeoutSec $([REST]::Timeout) -Verbose }
#TEAMS
[PSCustomObject]Teams([string]$Url, [string]$Collection, [string]$Project) { return $($this.Get($(Combine #($Url, $Collection, $Project, "_settings/teams?__rt=fps&__ver=2")))).fps.dataProviders.data.'ms.vss-tfs-web.team-data' }
[PSCustomObject]Teams([string]$Collection, [string]$Project) { return $this.Teams($this.Url, $Collection, $Project) }
[PSCustomObject]Teams([string]$Project) { return $this.Teams($this.Url, $this.Collection, $Project) }
[PSCustomObject]Teams() { return $this.Teams($this.Url, $this.Collection, $this._Project) }
#TEAM MEMBERS
[PSCustomObject]TeamMembers([string]$Url, [string]$Collection, [string]$Project, [string]$TeamId) { return $this.Get($(Combine #($Url, $Collection, $Project, "_api/_identity/ReadGroupMembers?__v=5&scope=$($TeamId)&readMembers=true&scopedMembershipQuery=1"))) }
[PSCustomObject]TeamMembers([string]$Collection, [string]$Project, [string]$TeamId) { return $this.TeamMembers($this.Url, $Collection, $Project, $TeamId) }
[PSCustomObject]TeamMembers([string]$Project, [string]$TeamId) { return $this.TeamMembers($this.Url, $this.Collection, $Project, $TeamId) }
[PSCustomObject]TeamMembers([string]$TeamId) { return $this.TeamMembers($this.Url, $this.Collection, $this._Project, $TeamId) }
#TEAM MEMBER POST
[PSCustomObject]TeamMemberPost([string]$Url, [string]$Collection, [string]$Project, [string]$TeamId, [string]$Domain, [string]$Name) { $body = '{{''newUsersJson'':''[\''{0}\\\\{1}\'']'',''existingUsersJson'':''[]'',''groupsToJoinJson'':''[\''{2}\'']'',''aadGroupsJson'':''[]''}}' -f ($Domain, $Name, $TeamId); return $this.Post($(Combine #($Url, $Collection, $Project, "_api/_identity/AddIdentities?__v=5")), $body) }
[PSCustomObject]TeamMemberPost([string]$Collection, [string]$Project, [string]$TeamId, [string]$Domain, [string]$Name) { return $this.TeamMemberPost($this.Url, $Collection, $Project, $TeamId, $Domain, $Name) }
[PSCustomObject]TeamMemberPost([string]$Project, [string]$TeamId, [string]$Domain, [string]$Name) { return $this.TeamMemberPost($this.Url, $this.Collection, $Project, $TeamId, $Domain, $Name) }
[PSCustomObject]TeamMemberPost([string]$TeamId, [string]$Domain, [string]$Name) { return $this.TeamMemberPost($this.Url, $this.Collection, $this._Project, $TeamId, $Domain, $Name) }
}
These are the REST-API calls I used for.
#TEAMS returns all teams of a project as json. The call also gives you the $TeamId
#TEAM MEMBERS give you all members of a team
#TEAM MEMBER POST allows you to add you new members. Important: the members must be known by Azure DevOps, that means they need to be in your domain (I don't know how it is organized in azure devops service)
How to use: (but this in the same file like the REST class or load the REST class as module or file before)
#ADD = LIST OF VALID AND KNOWN MEMBERS OF YOUR AZURE DEVOPS SERVICE (STORE IT IN A .TXT FILE OR SOMETHING)
$ADD = #("member1#xyz.com", "member2#xyz.com")
#INITIALIZE REST API
$REST = [REST]::new($PAT, $Uri, $Collection, $Project) #$PAT ~ "atfghfrhfdgdwnx6jnyrculcmaas2g5j6rrogpmn7aza266hrudsahq"; $Uri = https://server.com
#REQUEST TEAMS
$result = $REST.Teams()
$team = $result.team
#REQUEST TEAM MEMBERS
$result = $REST.TeamMembers($team.id)
$members = $result.identities.MailAddress
#ADD MISSING MEMBERS TO TEAM
foreach ($item in $ADD) {
if (-not $members.Contains($item)) {
Write-Host "[ps1] add: '$item'" -ForegroundColor Yellow
#POST ADD MEMBER
$name = $item.Replace($mail, "")
$result = $REST.TeamMemberPost($team.id, $domain, $name)
if ("AddedIdentities" -in $result.PSobject.Properties.Name) { Write-Host "[ps1] successful added: $($result.AddedIdentities.DisplayName) ($($result.AddedIdentities.TeamFoundationId))" -ForegroundColor Green }
else { Write-Host "[ps1] fail to add: '$name'" -ForegroundColor Red }
}
}
I take the snippts from my script. I don't have the time to test this stuff, so please expect errors.
How to find out the correct URLs by your self:
Open Browser (I used Edge)
Press F12
Go to Network
Navigate to the event you want to observe
Clear the list
Execute the event (click button)
Check out the GET/POST with application/json like in sceen shot:
If it is a GET/POST Event you can display the the transfered json under text
{
"newUsersJson": "[\"Domain\\\\user\"]",
"existingUsersJson": "[]",
"groupsToJoinJson": "[\"2d1dfa03-a108-4421-958a-bdsfdsf161696\"]",
"aadGroupsJson": "[]"
}
Hope this helps.

You can use member add api to user to team members.
PUT https://vsaex.dev.azure.com/{organization}/_apis/GroupEntitlements/{groupId}/members/{memberId}?api-version=5.1-preview.1
When you go to the Permissions under Project Settings, You will find the team is actually listed as a group. So i tried using team Id for the groupId in above api. And it worked.
After testing,the memeberId is actually the user id.
You can get the user id with below Get User Entitlements api: check here for details.
GET https://vsaex.dev.azure.com/{organization}/_apis/userentitlements?top={top}&skip={skip}&filter={filter}&sortOption={sortOption}&api-version=5.1-preview.2
Then you can call above member add api to add user to teams.
$uri ="https://vsaex.dev.azure.com/{ORG}/_apis/GroupEntitlements/{teamid}/members/{userid}?api-version=5.1-preview.1"
$connectionToken="PAT"
$base64AuthInfo= [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes(":$($connectionToken)"))
# Invoke the REST call and capture the results (notice this uses the PATCH methodg
$result = Invoke-RestMethod -Uri $group -Headers #{Authorization=("Basic {0}" -f $base64AuthInfo)} -Method put
If you cannot hit https://vsaex.dev.azure.com. You may need to check if your {PAT} has the all the permission scopes to perform add member action. Check here for more information about PAT.
There is a lack of information about vsaex. but i guess vsaex is the server domain for user ad data. As Microsoft manage user ad data information in a separate server from other data.

Not 100% an answer to your question, but maybe it can help you or other people.
Recently I had to add user to a team in an AzDo project. I had the email for the users and the name of the team.
I used the following Powershell code in AzDo version M183_20210320.1:
$PAT = "my-path"; # get your Personal access token https://learn.microsoft.com/en-us/azure/devops/organizations/accounts/use-personal-access-tokens-to-authenticate?view=azure-devops&tabs=preview-page
$Headers += #{Authorization = 'Basic ' + [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(":$($PAT)")) };
$Collection = 'my-organisation';
$Project = 'my-project';
$Timeout = 30;
$TeamToAddUser = "my-team-name>";
$EmailToAddToTeam = "my#email.com";
# get project id
$ProjectId = (Invoke-RestMethod -Uri "https://dev.azure.com/$($Collection)/_apis/projects/$($Project)?api-version=6.0" -Method GET -ContentType "application/json;charset=utf-8" -Headers $Headers -TimeoutSec $Timeout).id;
Write-Host "ProjectId: $ProjectId";
# get project descriptor
$ProjecDescriptor = (Invoke-RestMethod -Uri "https://vssps.dev.azure.com/$($Collection)/_apis/graph/descriptors/$($ProjectId)?api-version=6.0-preview" -Method GET -ContentType "application/json;charset=utf-8" -Headers $Headers -TimeoutSec $Timeout).value;
Write-Host "ProjecDescriptor: $ProjecDescriptor";
# get all teams in project
$TeamsInProject = (Invoke-RestMethod -Uri "https://vssps.dev.azure.com/$($Collection)/_apis/graph/groups?scopeDescriptor=$($ProjecDescriptor)&api-version=6.0-preview" -Method GET -ContentType "application/json;charset=utf-8" -Headers $Headers -TimeoutSec $Timeout).value;
Write-Host "TeamsInProject: $($TeamsInProject | forEach { "`n - $($_.displayName) : $($_.descriptor)" }) `n";
# get the team
$Team = $TeamsInProject | Where-Object { $_.displayName -eq $TeamToAddUser }
Write-Host "Team: $($Team.displayName) : $($Team.descriptor)";
# get user id from email
$User = (Invoke-RestMethod -Uri "https://vsaex.dev.azure.com/$($Collection)/_apis/userentitlements?api-version=6.0-preview.3&`$filter=name eq '$(${EmailToAddToTeam})'" -Method GET -ContentType "application/json;charset=utf-8" -Headers $Headers -TimeoutSec $Timeout).members[0];
Write-Host "User to add user: $($User.user.displayName) : $($User.user.originId)";
# add user to team
$body = #{
"originId" = $User.user.originId
};
$result = (Invoke-RestMethod -Uri "https://vssps.dev.azure.com/$($Collection)/_apis/graph/Users?groupDescriptors=$($Team.descriptor)&api-version=6.0-preview" -Method POST -ContentType "application/json;charset=utf-8" -Headers $Headers -Body $($body | ConvertTo-Json -Depth 10 -Compress) -TimeoutSec $Timeout)

After spending hours to do this via API I found a solution, You can use
POST https://vssps.dev.azure.com/{organization}/_apis/graph/users?groupDescriptors={groupDescriptor}&api-version=6.0-preview.1
you can add email in a requestbody, JSON will look like
{
"principalName": "yourmail#goeshere.com"
}
For getting the group descriptor use below GET call
GET https://vssps.dev.azure.com/{organization}/_apis/graph/groups?api-version=6.0-preview.1
Check for group with "displayName" as "{your project name} Team" in the response and take group descriptor of that group.
If you are using Postman to make this call, select basic Auth as authorization and give username empty and password as the PAT token.

Related

Export all API names Azure APIM

I am trying to export all the url endpoints of my APIs which are stored in Azure APIM.
I need them listed in the format [POST]: https://apim-resource-name.azure-api.net/api-name/api-function, or something similar, so I am trying to enumerate all the endpoints first and the VERB that can be used much like what SwaggerUI or one of the OpenAPI docs might show.
I have this code so far but cannot get the output I would like, am I on the right track or is there any easier way to do this even via UI? I have also tried exporting the template and parsing the script, but it seemed overly complicated for this task.
#az login
$token = az account get-access-token | ConvertFrom-Json
$token = $token.accessToken -replace "`n","" -replace "`r",""
$headers = #{Authorization="Bearer $token.accessToken"}
$subscriptionId = 'subscriptionid'
$resourceGroupName = 'resourcegroupname'
$resourceName = 'resourcename'
$apiName = 'apiname'
$url_getapiexport = "https://management.azure.com/subscriptions/$subscriptionId/resourceGroups/$resourceGroupName/providers/Microsoft.ApiManagement/service/$resourceName/apis/$apiName`?format=swagger-link&export=true&api-version=2021-08-01" #GET
$url_getapiexport
#Invoke-RestMethod -Uri $url_getapiexport -Method GET -Headers #{Authorization="Bearer $token"} -ContentType "application/json"
$url_getapibytags = "https://management.azure.com/subscriptions/$subscriptionId/resourceGroups/$resourceGroupName/providers/Microsoft.ApiManagement/service/$resourceName/apisByTags?&includeNotTaggedApis=true&api-version=2021-08-01" #GET
$url_getapibytags
$api_tags = Invoke-RestMethod -Uri $url_getapibytags -Method GET -Headers #{Authorization="Bearer $token"} -ContentType "application/json"
$api_tags
foreach ($api in $api_tags) {
write-host API: $api.value.api.name
}
So if you want to export all APIM APIs and their service URLs you can do like this (replace xxx with correct values). Example in PHP
<?php
echo "[INFO] API Names and Service Urls \n\n";
$apiList = shell_exec('az apim api list --resource-group "xxx" --service-name "xxx" ');
$apis = json_decode($apiList, true);
$cli = "az rest --method get --url";
foreach ($apis as $api)
{
$name = $api["name"];
$apiRequest = "$cli " . "\"https://management.azure.com/subscriptions/xxx/resourceGroups/xxx/providers/Microsoft.ApiManagement/service/xxx/apis/$name?api-version=2021-08-01\"";
$json = shell_exec($apiRequest);
$apiInfo = json_decode($json, true);
$displayName = $apiInfo["properties"]["displayName"];
$serviceUrl = $apiInfo["properties"]["serviceUrl"];
printf("%s: %s \n", $displayName, $serviceUrl);
}
?>

How to get all key secrets after rest api collect all secrets in Azure Key Vault

Have a nice day everyone!
I have a VMware Windows Which has permission in key vault and I want to collect all key secrets but the code below when it finished just has ID + Attributes not consist value of Key secrets. Anyone can help me finish the code below to get all key secrets.
Many thanks for your help!
$RresourceUrl = 'dddd.vault.azure.net'
# Compose REST request.
$response = Invoke-WebRequest -Uri 'http://169.254.111.211/metadata/identity/oauth2/token?api-version=2018-02-01&resource=https%3A%2F%2Fvault.azure.net' -Method GET -Headers #{Metadata="true"}
$OAuth = $response.Content | ConvertFrom-Json
# Check if authentication was successfull.
if ($OAuth.access_token) {
#Format headers.
$HeaderParams = #{
'Content-Type' = "application\json"
'Authorization' = "Bearer $($OAuth.access_token)"
}
# Create an empty array to store the result.
$QueryResults = #()
$Uri = "https://$RresourceUrl/secrets?api-version=2016-10-01"
# Invoke REST method and fetch data until there are no pages left.
do {
$Results = Invoke-WebRequest -Uri $Uri -Method GET -Headers $HeaderParams | ConvertFrom-Json
$Results.nextLink
if ($Results.value) {
$QueryResults += $Results.value
}
else {
$QueryResults += $Results
}
$Uri = $Results.nextLink
} until (!($Uri))
# Return the result.
$QueryResults | Export-Csv -NoTypeInformatio *\Documents\Tesst.csv
}
else {
Write-Error "No Access Token"
}
This is my final code maybe isn't optimized but it worked.
$RresourceUrl = 'devakv01.vault.azure.net'
# Compose REST request.
$response = Invoke-WebRequest -Uri 'http://169.254.111.211/metadata/identity/oauth2/token?api-version=2018-02-01&resource=https%3A%2F%2Fvault.azure.net' -Method GET -Headers #{Metadata="true"}
$OAuth = $response.Content | ConvertFrom-Json
# Check if authentication was successfull.
if ($OAuth.access_token) {
#Format headers.
$HeaderParams = #{
'Content-Type' = "application\json"
'Authorization' = "Bearer $($OAuth.access_token)"
}
# Create an empty array to store the result.
$QueryResults = #()
$Uri = "https://$RresourceUrl/secrets?api-version=2016-10-01"
# Invoke REST method and fetch data until there are no pages left.
do {
$Results = Invoke-WebRequest -Uri $Uri -Method GET -Headers $HeaderParams | ConvertFrom-Json
$Results.nextLink
if ($Results.value) {
$QueryResults += $Results.value
}
else {
$QueryResults += $Results
}
$Uri = $Results.nextLink
} until (!($Uri))
# Return the result.
$QueryResults
}
else {
Write-Error "No Access Token"
}
# get Key after to have secrets name
$output = ForEach ($nameSecret in $QueryResults.id)
{
$Resultskey = Invoke-WebRequest -Uri $($nameSecret+'?api-version=2016-10-01') -Method GET -Headers $HeaderParams | ConvertFrom-Json
$Resultskey
}
$output | Export-Csv -NoTypeInformatio *\$RresourceUrl.csv

Can we add label to tiles on Azure Dashboard [duplicate]

Is it possible to show custom information in azure dashboards?
I was searching on how to add custom content in azure dashboards but did not find anything. The only thing close was the markdown tile which allows html to be displayed.
With this in mind and after a lot of digging I found a solution:
Basically we needed a custom tile that displays data retrieved from our REST api.
1. Create a new, empty 'Markdown' tile on a new or existing dashboard, give it a 'Title'
2. Share the dashboard
3. Navigate to All services, Filter by 'dashboards' in the ResourceGroup filter
- Click on the dashboard which contains the 'Markdown' tile
- Take a note of the 'RESOURCE ID'
In our scenario, we used Azure Automation Runbooks. In this scenario we utilized the Azure Resource Manager REST api.
4. Create a new RunBook [Powershell Runbook]
The following steps concern the following:
- Login to the ResourceManagerAPI
- Get Azure Resource by ID [The Resource ID above]
- Update Azure Resource by ID [The Resource ID above]
Before we continue, we need to get our client credentials. To do so:
- Click on Cloud Shell in the Portal Menu bar
- Type 'az'
- Type 'az ad sp create-for-rpac -n "runbooks"' //runbooks is just a name, feel free to input a different string
- The above command should list out the credentials needed. If an error occurs, kindly contact your Azure admin and run it from their account.
5. In your empty powershell runbook, add the following 2 variables:
$ExpectedTileName = "Extension/HubsExtension/PartType/MarkdownPart"
$MarkdownTileTitle = "<The Markdown title you've set in the first step>"
6. Getting the Access_Token [The variables <> represent the values retrieved from the previous step]
#Get Bearer Token
$TenantId = '<Your tenantID>'
$LoginUri = "https://login.microsoftonline.com/"+$TenantId+"/oauth2/token"
$params = #{
"grant_type"="client_credentials";
"client_id"="<appId>";
"client_secret"="<password>";
"resource"="https://management.azure.com";
}
$LoginResponse = Invoke-RestMethod -Uri $LoginUri -Method Post -Body $params
$Access_Token = $LoginResponse.access_token;
$Access_TokenString = "Bearer " + $Access_Token
7. Getting the DashboardResource by ResourceID:
#Get Resource
$RMUri = "https://management.azure.com/"+ $DashboardId +"?api-version=2015-08-01-preview"
$DashboardResource = (Invoke-RestMethod -Uri $RMUri -Method Get -Headers #{'Authorization'=$Access_TokenString}) | ConvertTo-Json -Depth 100 | ConvertFrom-Json
8. Looping through all tiles within the dashboard. Please note that tiles are not contained within an array, thus you may need to increase/decrease the length of the for loop.
#Loop through all tiles within the dashboard
$Parts = $DashboardResource.properties.lenses.0.0.parts
For ($i=0; $i -lt 200; $i++)
{
$Part = $Parts | Select-Object -Property $i.toString()
if($Part.$i)
{
if($Part.$i.metadata.type -eq $ExpectedTileName -And $Part.$i.metadata.settings.content.settings.title -eq $MarkdownTileTitle)
{
$Part.$i.metadata.settings.content.settings.content = <CustomValue ex: invoke a get request to your api>
}
}
else
{
break
}
}
9. Finally we need to update the dashboard resource
#Update Resource
$UpdateUri = "https://management.azure.com/"+ $DashboardId +"?api-version=2015-08-01-preview"
$JsonValue = $DashboardResource | ConvertTo-Json -Depth 100
Invoke-RestMethod -Uri $UpdateUri -Method Put -Headers #{'Authorization'=$Access_TokenString; 'Content-type'='application/json'} -Body $JsonValue
To sum it up:
$ExpectedTileName = "Extension/HubsExtension/PartType/MarkdownPart"
$MarkdownTileTitle = "<The Markdown title you've set in the first step>"
#Get Bearer Token
$TenantId = '<Your subscriptionID>'
$LoginUri = "https://login.microsoftonline.com/"+$TenantId+"/oauth2/token"
$params = #{
"grant_type"="client_credentials";
"client_id"="<appId>";
"client_secret"="<password>";
"resource"="https://management.azure.com";
}
$LoginResponse = Invoke-RestMethod -Uri $LoginUri -Method Post -Body $params
$Access_Token = $LoginResponse.access_token;
$Access_TokenString = "Bearer " + $Access_Token
#Get Resource
$RMUri = "https://management.azure.com/"+ $DashboardId +"?api-version=2015-08-01-preview"
$DashboardResource = (Invoke-RestMethod -Uri $RMUri -Method Get -Headers #{'Authorization'=$Access_TokenString}) | ConvertTo-Json -Depth 100 | ConvertFrom-Json
#Loop through all tiles within the dashboard
$Parts = $DashboardResource.properties.lenses.0.0.parts
For ($i=0; $i -lt 200; $i++)
{
$Part = $Parts | Select-Object -Property $i.toString()
if($Part.$i)
{
if($Part.$i.metadata.type -eq $ExpectedTileName -And $Part.$i.metadata.settings.content.settings.title -eq $MarkdownTileTitle)
{
$Part.$i.metadata.settings.content.settings.content = <CustomValue ex: invoke a get request to your api>
}
}
else
{
break
}
}
#Update Resource
$UpdateUri = "https://management.azure.com/"+ $DashboardId +"?api-version=2015-08-01-preview"
$JsonValue = $DashboardResource | ConvertTo-Json -Depth 100
Invoke-RestMethod -Uri $UpdateUri -Method Put -Headers #{'Authorization'=$Access_TokenString; 'Content-type'='application/json'} -Body $JsonValue
Conclusion
With this newly created runbook we can now schedule it to run every 1 hour. In our case, 1 hour was too much. The following article shows how we can schedule the runbook to run every 1 minute.
https://blogs.technet.microsoft.com/stefan_stranger/2017/06/21/azure-scheduler-schedule-your-runbooks-more-often-than-every-hour/

Azure REST API list key vault secrets has maxresults limited to 25

I'm trying to get a list of all the secrets in each of my key vaults and I'm using Microsofts documentation at this URL.
https://learn.microsoft.com/en-us/rest/api/keyvault/getsecrets/getsecrets#secretlistresult
It states that if you do not set maxresults it will default to 25. It does however throw this error in my powershell script when i try to set it higher than 25.
{"error":{"code":"BadParameter","message":"invalid maxresults"}}
From the documentation the endpoint does not appear to contain any pagination or way to get more than 25 random secrets. This seems to make the endpoint pretty useless as there are no ways to filter the listings.
The command I'm using to get the list is this.
$uri = ""https://$($Vault).vault.azure.net/secrets?api-version=7.1&maxresults=26""
Invoke-RestMethod -Method Get -Uri $uri -Headers $headers
As Gaurav said in the comment, you need to use nextLink to get the result of next page.
There is my test code with do-until:
$LoginUrl = "https://login.microsoft.com"
$RresourceUrl = "https://vault.azure.net/.default"
$ClientID = ""
$ClientSecret = ""
$TenantName = ""
# Compose REST request.
$Body = #{ grant_type = "client_credentials"; scope = $RresourceUrl; client_id = $ClientID; client_secret = $ClientSecret }
$OAuth = Invoke-RestMethod -Method Post -Uri $LoginUrl/$TenantName/oauth2/v2.0/token -Body $Body
# Check if authentication was successfull.
if ($OAuth.access_token) {
# Format headers.
$HeaderParams = #{
'Content-Type' = "application\json"
'Authorization' = "Bearer $($OAuth.access_token)"
}
# Create an empty array to store the result.
$QueryResults = #()
$Uri = "https://<your-key-vault-name>.vault.azure.net/secrets?api-version=7.1"
# Invoke REST method and fetch data until there are no pages left.
do {
$Results = Invoke-RestMethod -Headers $HeaderParams -Uri $Uri -UseBasicParsing -Method "GET" -ContentType "application/json"
$Results.nextLink
if ($Results.value) {
$QueryResults += $Results.value
}
else {
$QueryResults += $Results
}
$Uri = $Results.nextLink
} until (!($Uri))
# Return the result.
$QueryResults
}
else {
Write-Error "No Access Token"
}

Azure DevOps REST API returns a 403 when using the system OAuth token during a build

I'm running a script:
# Variables
$organization = "****"
$project = "****"
$repositoryId = "****"
$pullRequestId = $env:BUILD_PULLREQUEST_ID
$pat = "Bearer $env:System_AccessToken"
$featureReleaseUrl = "http://" + $env:prSourceBranchName + ".azurewebsites.net"
$body = #"
{
"comments": [
{
"content": "Link naar feature release $featureReleaseUrl"
}
]
}
"#
$createThreadInPRUrl = "https://dev.azure.com/$organization/$project/_apis/git/repositories/$repositoryId/pullRequests/$pullRequestId/threads?api-version=5.0"
if ($pullRequestId) {
Invoke-RestMethod -Uri $createThreadInPRUrl -Headers #{Authorization = $pat} -Body $body -Method Post -ContentType 'application/json'
}
When it runs it returns a:
##[error]The remote server returned an error: (403) Forbidden.
I've created a Personal Access Tokens in my personal settings.
I've also created this script:
# Variables
$organization = "****"
$project = "****"
$buildId = $****
$pat = "Bearer $env:System_AccessToken"
if (!$env:Build_PullRequest_SourceBranchName) {
$retrieveSourceBranchFromBuildURL = "https://dev.azure.com/$organization/$project/_apis/build/builds/$buildId" + "?api-version=5.0"
$buildInformation = Invoke-RestMethod -Uri $retrieveSourceBranchFromBuildURL -Headers #{Authorization = $pat } -Method Get -ContentType 'application/json'
$SourceBranchFromBuild = $buildInformation.sourceBranch.split('/')[-1]
Write-Host "### no Build PullRequest SourceBranchName available ###"
Write-Host "##vso[task.setvariable variable=prSourceBranchName;]"$SourceBranchFromBuild
}
And this runs fine. The difference between the first and second script is that the first is a POST and the second a GET. But they both use the $pat token.
Even though the token you used is System.AccessToken, if you don't have access permission of Pull Request, you will also could not operate it.
Go Project Setting--> Repositories--> Repository you want to access, locate your account or the group you are in. Check the permission state of Contribute to pull requests.
You must have this Contribute to pull requests permission allowed, so that you can add the comment to PR.

Resources