VSTS Rest API queries for linked work items in Powershell - excel

Using REST APIs of VSTS in PowerShell, I wanted to create a report on my VSTS Team Project which consists of the details of the Work Item Linking relationships of all workitems under particular "Iteration Path" and "Area Path".
Ex: Epics→Features→UserStories. Since there are parent/child relationships between Epics & Features and also between Features & UserStories.
So the input would be Iteration Path and Area Path, and the corresponding output would be a report (.csv or .xls) which has all the details of these workitems and their relationships.
Could someone let me know on how to achieve this using REST APIs in PowerShell?
I wrote the code for getting the hierarchical workitems in a Team Project but need to modify to filter the the list based on a given Iteration and Area paths.
Also when executing the below code, the string comparison in the query(due to presence of '') is returning an error in the power-shell.
Error: Invoke-RestMethod : {"count":1,"value":{"Message":"After parsing a value an unexpected character was encountered: G.
Path 'query', line 1, position 163.\r\n"}}
$vstsAccount = "xxxx"
$projectName = "xxxx"
$user = "xxxx"
$token = "xxxx"
# Base64-encodes the Personal Access Token (PAT) appropriately
$base64AuthInfo = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(("{0}:{1}" -f $user,$token)))
# Construct the REST URL to obtain Build ID
$uri = "https://$($vstsAccount).visualstudio.com/$($projectName)/_apis/wit/wiql?api-version=1.0"
$body = "{'query': 'Select [System.Id], [System.WorkItemType], [System.Title], [System.AssignedTo], [System.State] From WorkItemLinks WHERE
((Source.[System.TeamProject] = '$projectName' and Source.[System.State] <> 'Removed') and (Source.[System.WorkItemType] = 'Epic') and
([System.Links.LinkType] = 'System.LinkTypes.Hierarchy-Forward') and (Target.[System.WorkItemType] = 'UserStory') mode(Recursive)'}"
# Invoke the REST call and capture the results (notice this uses the POST method)
$result = Invoke-RestMethod -Uri $uri -Method Post -ContentType "application/json" -Headers #{Authorization=("Basic {0}" -f $base64AuthInfo)} -Body $body

Modify part of your code like this:
$query = "Select
[System.Id],
[System.WorkItemType],
[System.Title],
[System.AssignedTo],
[System.State]
From WorkItemLinks
WHERE (
Source.[System.TeamProject] = '$projectName'
AND Source.[System.State] <> 'Removed'
) AND (
Source.[System.WorkItemType] = 'Epic')
AND ([System.Links.LinkType] = 'System.LinkTypes.Hierarchy-Forward'
) AND (Target.[System.WorkItemType] = 'UserStory') mode (Recursive)
"
$body = #{query=$query} | ConvertTo-Json
$result = Invoke-RestMethod -Uri $uri -Method Post -ContentType "application/json" -Headers #{Authorization=("Basic $base64AuthInfo")} -Body $body

Related

Azure devops release edit bulk 110

Hello I have 145 releases
And I have to do an equal action for everyone for example add a certain variable and edit a certain line.
Is there a way to control everyone with a script?
I checked for a template, but it creates the release from 0 and does not edit it.
Can a script in PS or Python be a solution? I did not find any information on Google about it, other than an export release template.
Azure devops release edit bulk 110
You could use the REST API Definitions - Update to update the release pipeline:
PUT https://vsrm.dev.azure.com/{organization}/{project}/_apis/release/definitions/{DefinitionsId}?api-version=6.0
And if we want to batch modify the release pipeline, we need to get each ID of the release pipeline with REST API Definitions - List:
GET https://vsrm.dev.azure.com/{organization}/{project}/_apis/release/definitions?api-version=6.0
Then we could iterate through each Rlease ID obtained.
I provide a rough code for your better reading:
$connectionToken="PAT"
$base64AuthInfo= [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes(":$($connectionToken)"))
$ReleasePipelineUrl = "https://vsrm.dev.azure.com/{organization}/{project}/_apis/release/definitions?api-version=6.0"
Write-Host "URL: $ReleasePipelineUrl"
$ReleasePipelines = (Invoke-RestMethod -Uri $PipelineUrl -Method Get -UseDefaultCredential -Headers #{Authorization=("Basic {0}" -f $base64AuthInfo)})
$ReleasePipelinesId = $ReleasePipelines.value.id
Write-Host "ReleasePipelinesId = $ReleasePipelinesId"
ForEach ($Pt in $ReleasePipelinesId)
{
$baseUrl = "https://vsrm.dev.azure.com/{organization}/{project}/_apis/release/definitions/$($Pt)?api-version=6.0"
$pipeline = (Invoke-RestMethod -Uri $baseUrl -Method Get -UseDefaultCredential -Headers #{Authorization=("Basic {0}" -f $base64AuthInfo)})
Write-Host "URL: $baseUrl"
$pipeline.variables.TestValue.value = "$buildNumber"
####****************** update the modified object **************************
$json = #($pipeline) | ConvertTo-Json -Depth 99
Write-Host "URL: $json "
$updatedef = Invoke-RestMethod -Uri $url -Method Put -Body $json -ContentType "application/json" -Headers #{Authorization = "Bearer $env:SYSTEM_ACCESSTOKEN"}
write-host "=========================================================="
Write-host "The value of Varialbe 'TestValue' is updated to" $updatedef.variables.TestValue.value
}

Current Iteration Query Script Devops Looping Through Projects

I'm trying to put a script together that will let me loop through projects and create queries relevant to the current iteration.
I can loop through and create queries until it comes to looking at the iteration path and then it doesn't seem to like it.
When I run my script all of the other queries are created, and this one is as well, but it brings up an error when I go to look at it because it's posting that it's not in the right form. How can I get this query to loop through like the others and bring back the appropriate team in the process?
I'm assuming that somehow I've got to put project and team as variables? I tried $project as well but that just presented a similar error. Although I'm also confused about about the "" since the code doesn't like that either, and it won't even create the query when I've tried that.
$JSON27 = #'
{
"name": "Current Sprint Query", "wiql": "SELECT [System.Id], [System.WorkItemType], [System.Title], [System.AssignedTo], [System.State],[System.Tags],[Microsoft.VSTS.Scheduling.OriginalEstimate],[Microsoft.VSTS.Scheduling.CompletedWork],[System.IterationPath] FROM workitems WHERE [System.TeamProject] = #project AND [System.WorkItemType] <> '' AND [System.State] = 'Closed' AND [System.IterationPath] = #CurrentIteration('[Project]/Team') ORDER BY [System.WorkItemType]"
}
'#
$response27 = Invoke-RestMethod -Uri $url1 -Headers #{Authorization = "Basic $token"} -Method Post -Body $JSON27 -ContentType application/json
Please refer to the following script to get the project name first, and then create a query by looping through the obtained project name.
Please use #CurrentIteration('[Project]\\Team') instead of #CurrentIteration('[Project]/Team') or #CurrentIteration('[Project]\Team')
$token = "your PAT"
$url="https://dev.azure.com/org name/_apis/projects?api-version=6.0"
$token = [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes(":$($token)"))
$response = Invoke-RestMethod -Uri $url -Headers #{Authorization = "Basic $token"} -Method Get -ContentType application/json
echo $url
foreach ($project in $response.value.name){
$url3="https://dev.azure.com/org name/$($project)/_apis/wit/queries/My Queries?api-version=6.1-preview.2"
echo $url1
$body = "{
`"name`": `"query name`",
`"wiql`": `"Select [System.Id], [System.State] From WorkItems where [System.IterationPath] = #CurrentIteration('[$project]\\$project Team') `"
}"
echo $body
$response3 = Invoke-RestMethod -Uri $url3 -Headers #{Authorization = "Basic $token"} -Method Post -Body $body -ContentType application/json
echo $response3
}
Summarizing the discussion in comments as an answer below:
So, as the error message calls out, I think the issue is that you have #CurrentIteration('[Project]/Team') in your query instead of #CurrentIteration('[Project]\Team').
Your query should be this instead:
$JSON27 = #'
{
"name": "Current Sprint Query", "wiql": "SELECT [System.Id], [System.WorkItemType], [System.Title], [System.AssignedTo], [System.State],[System.Tags],[Microsoft.VSTS.Scheduling.OriginalEstimate],[Microsoft.VSTS.Scheduling.CompletedWork],[System.IterationPath] FROM workitems WHERE [System.TeamProject] = #project AND [System.WorkItemType] <> '' AND [System.State] = 'Closed' AND [System.IterationPath] = #CurrentIteration('[Project]\Team') ORDER BY [System.WorkItemType]"
}
'#
EDIT:
By default, shared queries and new queries are scoped to the current project. For querying across multiple projects, you can add the TeamProject field to filter to a select number of projects as:
..
AND [System.TeamProject] IN ('Project 1', 'Project 2', 'Project 3')
However, using the #CurrentIteration macro would need you to select a Team and may not work across Projects and Teams. Check this idea on Developer Community for more details.

Update defaultBranch of build definition via Azure DevOps API

I need to update the defaultBranch of build definition via Azure API.
The is the documentation for PUT request:
https://learn.microsoft.com/en-us/rest/api/azure/devops/build/definitions/update?view=azure-devops-rest-5.1
The problem is that I don't know the minimum list pf params I need to send in order to create a working request.
I tried to export the actual request from the browser and modify only this field - status code is 200 but nothing was changed. I don't want to pass all filed I just want to modify the defaultBranch.
In the link you provided you have Request Body section, you can see there what you need to pass. I like to get the definition first (with Get API), change what I want, and then perform the update.
In PowerShell:
$pat = "YOUR-PAT"
$base64AuthInfo = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(("{0}:{1}" -f $user,"$pat")))
$headers = #{Authorization=("Basic {0}" -f $base64AuthInfo)}
$url = "https://dev.azure.com/{organization}/{project}/_apis/build/definitions/{definitionId}?api-version=5.1"
$build = Invoke-RestMethod -Uri $url -Method Get -ContentType application/json -Headers $headers
$newDefaultBranch = "test-branch"
$build.repository.defaultBranch = $newDefaultBranch
$json = $build | ConvertTo-Json -Depth 10
$response = Invoke-RestMethod -Uri $url -Method Put -ContentType application/json -Headers $headers -Body $json

How to make Invoke-RestMethod GET and PUT requests to Azure table storage using SAS key

This is a two-part question. I am in the process of automating tasks that a) require information from my Azure table and b) need to update specific entities in my Azure table. I've currently been able to accomplish this by using either of the 2 provided access keys but think this is an unsafe practice and want to define individual policies for different groups and so want to transition into using generated SAS keys.
a) I can currently use SAS policies to retrieve the whole table and find the information I need but I think a better method is to perform an individual query that only pulls the single entity that matches a specific property I'm looking for (e.g. pull all properties of an entity that matches a customer ID: "000000001"). How can I change my code to accomplish this?
$tableName = "accountTD"
$sasReadToken = '<SAS token here>'
$tableUri = "https://$storageAccount.table.core.windows.net/$tableName$sasReadToken"
$GMTTime = (Get-Date).ToUniversalTime().toString('R')
$header = #{
'x-ms-date' = $GMTTime;
Accept = 'application/json;odata=nometadata'
}
$finalResult = Invoke-WebRequest -Uri $tableUri -Headers $header -UseBasicParsing
$finalResult = $finalResult.Content | ConvertFrom-Json
$finalResult.value
b) I also need to update the same entity in the table and can't seem to figure out how to authorize it with my generated SAS key. I'm not sure whether to use Invoke-WebRequest or Invoke-RestMethod or how to go about either of them. Here's what I have so far based on my research.
function addUpdateEntity ($tableName, $PartitionKey, $RowKey, $entity){
$sasReadToken = '<SAS token here>'
$resource = "$tableName(PartitionKey='$PartitionKey',RowKey='$Rowkey')"
$tableUri = "https://$storageAccount.table.core.windows.net/$tableName$sasReadToken"
$GMTTime = (Get-Date).ToUniversalTime().toString('R')
$header = #{
'x-ms-date' = $GMTTime;
Accept = 'application/json;odata=nometadata'
}
$body = $entity | ConvertTo-Json
$item = Invoke-RestMethod -Method PUT -Uri $tableUri -Headers $headers -Body $body -ContentType application/json
}
$mBody = #{
PartitionKey = "MPS02000"
RowKey = "2019-000101"
appUpdateMode = "1"
m_CustID = "000000001"
}
addUpdateEntity -TableName "atdMachines" -PartitionKey $mBody.PartitionKey -RowKey $mBody.RowKey -entity $mBody
Q1. Pull all properties of an entity that matches a customer ID
Answer: You can use $filter query expression. For example, I have 2 entities in my testTable:
I can get the entity whose Id equals to 00001 by making a request as following:
GET https://storagetest789.table.core.windows.net/testTable?{sastoken}&$filter=(Id eq '00001')
$storageAccount = "storagetest789"
$tableName = "testTable"
$sasReadToken = "?sv=2019-02-02&ss=t&sr***************D"
$filter = "`$filter=(Id eq '00001')"
$tableUri = "https://$storageAccount.table.core.windows.net/$tableName$sasReadToken&$filter"
$GMTTime = (Get-Date).ToUniversalTime().toString('R')
$header = #{
'x-ms-date' = $GMTTime;
Accept = 'application/json;odata=nometadata'
}
$finalResult = Invoke-WebRequest -Uri $tableUri -Headers $header -UseBasicParsing
$finalResult = $finalResult.Content | ConvertFrom-Json
$finalResult.value
Result:
Q2. Update the same entity in the table
Answer: Both Invoke-WebRequest and Invoke-RestMethod are suitable for making a HTTP request here. I find some mistakes in your scripts, here is the fixed one:
function addUpdateEntity ($tableName, $PartitionKey, $RowKey, $entity){
$storageAccount = "storagetest789"
$tableName = "testTable"
# Need write access
$sasWriteToken = "?sv=2019-02-02&ss=t&s*****************************D"
$resource = "$tableName(PartitionKey='$PartitionKey',RowKey='$Rowkey')"
# should use $resource, not $tableNmae
$tableUri = "https://$storageAccount.table.core.windows.net/$resource$sasWriteToken"
# should be headers, because you use headers in Invoke-RestMethod
$headers = #{
Accept = 'application/json;odata=nometadata'
}
$body = $entity | ConvertTo-Json
$item = Invoke-RestMethod -Method PUT -Uri $tableUri -Headers $headers -Body $body -ContentType application/json
}
$mBody = #{
PartitionKey = "p1"
RowKey = "r1"
Id = "00001"
Value = "new value"
}
addUpdateEntity -TableName "atdMachines" -PartitionKey $mBody.PartitionKey -RowKey $mBody.RowKey -entity $mBody
Result:

How to add Extension Properties for Azure-AD Device Objects using PowerShell?

I want to add extension properties for device objects in Azure AD using Power-Shell. I have search a lot but found examples for only User objects.I have written a script and its successful for User Objects but am not be able to set extension properties for Device.
A command Set-AzureADUserExtension
exists for User but for devices, there is no such commands e.g
Set-AzureADDeviceExtension
(there is no command exists like it). Can anyone help me how to achieve this?How can i set extension properties for Device Objects?
I want to achieve something like this:
New-AzureADApplicationExtensionProperty -ObjectId $MyApp -Name "MyNewProperty" -DataType "String" -TargetObjects "Device";
Set-AzureADDeviceExtension -ObjectId $deviceId -ExtensionName "extension_0380f0f700c040b5aa577c9268940b53_MyNewProperty" -ExtensionValue "MyNewValue";
I was looking for exactly the same and I did not find anything then and today either. I had to use the Microsoft Graph API to add new extensions to the device object. The same for consulting.
Step 1: Install or import the azure module.
Install-Module AzureAD
or
Import-Module AzureAD
Step 2: Search Object and save ObjectID.
$ObjectID = (Get-AzureADDevice -SearchString 'Object-Name').ObjectId
Note: The "id" in the request is the "id" property of the device, not the "deviceId" property.
Step 3: Create App
https://portal.azure.com - Azure Active Directory - App registrations - New registration
Name: YourAppName
Supported account types: Accounts in this organizational directory only (Default Directory)
Redirect URI: (WEB) https://login.microsoftonline.com/common/oauth2/nativeclient
Step 4: Configure App
https://portal.azure.com - Azure Active Directory - App registrations - YourAppName
Certificates & secrets - New client secret
Save client secret value
API permissions - Add a permission - Microsoft Graph - Delegated permissions
Directory.AccessAsUser.All
Step 5: Get access_token
## Directory.AccessAsUser.All : Minimun privilege for Get, add, update and delete extensions. (https://learn.microsoft.com/en-us/graph/api/opentypeextension-post-opentypeextension?view=graph-rest-1.0)
$scopes = "Directory.AccessAsUser.All"
$redirectURL = "https://login.microsoftonline.com/common/oauth2/nativeclient"
$clientID = "YourAppIdClient"
$clientSecret = [System.Web.HttpUtility]::UrlEncode("YourAppClientSecret")
$authorizeUrl = "https://login.microsoftonline.com/common/oauth2/v2.0/authorize"
$requestUrl = $authorizeUrl + "?scope=$scopes"
$requestUrl += "&response_type=code"
$requestUrl += "&client_id=$clientID"
$requestUrl += "&redirect_uri=$redirectURL"
$requestUrl += "&response_mode=query"
Write-Host
Write-Host "Copy the following URL and paste the following into your browser:"
Write-Host
Write-Host $requestUrl -ForegroundColor Cyan
Write-Host
Write-Host "Copy the code querystring value from the browser and paste it below."
Write-Host
$code = Read-Host -Prompt "Enter the code"
$body = "client_id=$clientID&client_secret=$clientSecret&scope=$scopes&grant_type=authorization_code&code=$code&redirect_uri=$redirectURL"
$tokenUrl = "https://login.microsoftonline.com/common/oauth2/v2.0/token"
$response = Invoke-RestMethod -Method Post -Uri $tokenUrl -Headers #{"Content-Type" = "application/x-www-form-urlencoded"} -Body $body
$token = $response.access_token
Get Extensions device
$apiUrl = 'https://graph.microsoft.com/v1.0/devices/<ID-Object>/extensions' ## change <ID-Object> for your ObjectID.
(https://learn.microsoft.com/en-us/graph/api/device-get?view=graph-rest-1.0&tabs=cs)
$Data = Invoke-RestMethod -Headers #{Authorization = "Bearer $accessToken"} -Uri $apiUrl -Method Get
$Data.Value | fl
Add extensions device
$apiUrl = 'https://graph.microsoft.com/v1.0/devices/<ID-Object>/extensions'
$body = '{
"#odata.type": "microsoft.graph.openTypeExtension",
"id": "test.extension",
"name_extension": "example"
}'
Invoke-RestMethod -Headers #{Authorization = "Bearer $token"; "Content-type" = "application/json"} -Uri $apiUrl -Method Post -Body $body
Update extensions device
## Actualizar datos de una extensión
$apiUrl = 'https://graph.microsoft.com/v1.0/devices/<ID-Object>/extensions/test.extension' ## Extension ID to update
$body = '{
"#odata.type": "microsoft.graph.openTypeExtension",
"id": "test.extension",
"name_extension": "new_value"
}'
Invoke-RestMethod -Headers #{Authorization = "Bearer $token"; "Content-type" = "application/json"} -Uri $apiUrl -Method Patch -Body $body
Delete extensions device
$apiUrl = 'https://graph.microsoft.com/v1.0/devices/<ID-Object>/extensions/test.extension'
Invoke-RestMethod -Headers #{Authorization = "Bearer $token"; "Content-type" = "application/json"} -Uri $apiUrl -Method Delete

Resources