How to get the workitems from AzureDevOps with RestApi in Powershell - powershell

Param(
[string]$collectionurl = "https://dev.azure.com",
[string]$project = "projectname",
[string]$token = "PAT"
)
# Base64-encodes the Personal Access Token (PAT) appropriately
$base64AuthInfo =
[Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(("{0}" -f
$token)))
$baseUrl =
"$collectionurl/$project/_apis/wit/reporting/workitemrevisions?
includeLatestOnly=true&api-version=5.0-preview.2"
$response = (Invoke-RestMethod -Uri $baseUrl -Method Get -
UseDefaultCredential -Headers #{Authorization=("Basic {0}" -f
$base64AuthInfo)}).values
$wits = $response | where({$_.fields.'System.WorkItemType' -eq
'Task'}) # Only retrieve Tasks
$witrevisions = #()
foreach($wit in $wits){
$customObject = new-object PSObject -property #{
"WitID" = $wit.fields.'System.Id'
"rev" = $wit.fields.'System.Rev'
"Title" = $wit.fields.'System.Title'
"AssignedTo" = $wit.fields.'System.AssignedTo'
"ChangedDate" = $wit.fields.'System.ChangedDate'
"ChangedBy" = $wit.fields.'System.ChangedBy'
"WorkItemType" = $wit.fields.'System.WorkItemType'
}
$witrevisions += $customObject
}
$witrevisions | Select-Object `
WitID,
rev,
Title,
AssignedTo,
ChangedDate,
ChangedBy,
WorkItemType #|export-csv -Path E:\ashwin\devdata.csv -
NoTypeInformation
Write-Output $witrevisions
I want to display the workitems in my project to be displayed using powershell with the following Rest Api using my PAT.
https://dev.azure.com/{organization}/{project}/_apis/wit/workitems/{id}?api-version=5.1

How to get the workitems from AzureDevOps with RestApi in Powershell
The result will display in the output, you will find like following:
If you do not find above output, make sure you have workitem with Task type, because you have set the condition 'System.WorkItemType' -eq 'Task' in the powershell scripts.
On the other hand, you could export the work item list to a *.csv file, this part of the code is commented in powershell:
WorkItemType #| export-csv -Path G:\temp\WIT.csv -NoTypeInformation
If you want to create a *.csv file, you need to remove the # in that line, it should be :
WorkItemType | export-csv -Path G:\temp\WIT.csv -NoTypeInformation
Now, we could get that file in our local folder:
Note: The path is G:\temp is a local path, you should use the private agent, if you are using the hosted agent, you should copy that file from the hosted agent, and publish it to pipeline artifact.
Hope this helps.

Related

Get files that were changed in the repository push that triggered an Azure DevOps release pipeline

I'm trying to get the files added or changed in the repository push that triggered the release pipeline.
Using a PowerShell task, I've been able to identify the files that were changed if they are include in the push's most-recent commit:
$Instance = 'my.org'
$Collection = 'MyCollection'
$Project = 'MyProject'
$RepositoryId = 'my_repo'
$BaseUri = "https://{0}/{1}/{2}/_apis/git/repositories/{3}" -f $Instance, $Collection, $Project, $RepositoryId
$ReleaseDirectory = Join-Path $env:AGENT_RELEASEDIRECTORY $env:RELEASE_PRIMARYARTIFACTSOURCEALIAS
# the BUILD_BUILDID has the GUID for the latest commit
$env:BUILD_BUILDID | ForEach-Object {
$Uri = "$BaseUri/commits/$_/changes?api-version=5.0"
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
$Response = Invoke-WebRequest -Uri $Uri -UseDefaultCredentials -UseBasicParsing
$Content = $Response | ConvertFrom-Json
$Content.changes | Select-Object #{name='path';expression={$_.item.path}} | ForEach-Object {
$ChangedItemPath = Join-Path $ReleaseDirectory $_.path
Get-Item -Path $ChangedItemPath
} | Where-Object { $_.PSIsContainer -eq $false }
}
The Get Push Commits API method looks promising, but I can't find the pushId in the pipeline's environment variables.
Is the pushId available? Do I need to take different approach?

How to download a release file from a private git repository

I require to download a file programmatically from the releases section of a private GitHub repository, using powershell.
What I have tried so far:
$url = 'https://github.com/SatisfactoryModdingUE/UnrealEngine/releases/download/4.26.1-css-19/UnrealEngine-CSS-Editor-Win64-1.bin'
iwr -uri $url -outfile .\UnrealEngine-CSS-Editor-Win64-1.bin
This returns error: iwr : The request was aborted: The connection was closed unexpectedly..
This repo is a private repo. I have a username and personal access token that I can use to manually download, but I dont know how to apply these credentials to make the above download script work.
I found the solution. Thanks to:
Comment from #Justinas
API reference on list releases
API reference on the OAth authentication method
API reference on headers required for downloading binary assets
powershell script:
function get-latest-repo-release( [string]$gitPersonalAccessToken, [string]$owner, [string]$repo, [string]$dropFolder, $gitApiBase){
if ('' -eq "$gitApiBase"){
$gitApiBase = 'api.github.com'}
$parms = #{uri = "https://$gitApiBase/repos/$owner/$repo/releases"}
$headers = #{Accept = 'application/vnd.github.v3+json'}
if ('' -ne "$gitPersonalAccessToken"){
$headers['Authorization'] = "token $gitPersonalAccessToken"}
$parms['headers'] = $headers
$releases = iwr #parms | ConvertFrom-Json
$latestTag = $releases | sort -Property published_at -Descending | select -first 1 | select -expandProperty tag_name
$assets = $releases | ?{ $_.tag_name -eq $latestTag} | select -first 1 | select -expandProperty assets
$headers['Accept'] = 'application/octet-stream'
$assets | %{
$parms['uri'] = $_.url
$parms['outfile'] = "$dropFolder\$($_.name)"
iwr #parms}
$dropFolder}
$gitPersonalAccessToken = '<Redacted>'
$dropFolder = '<Redacted>'
$owner = 'SatisfactoryModdingUE'
$repo = 'UnrealEngine'
$outputDir = get-latest-repo-release -gitPersonalAccessToken $gitPersonalAccessToken -owner $owner -repo $repo -zipDropFolder $dropFolder
write-host "The latest releases can be found in this folder: $outputDir"

Appending to a URL with powershell

function Get-Data(){
[PSObject[]]$pid = ''
$getUri1 = 'https://playbook2.com/data/project/folder/28220'
$projectIds = wget $getUri1 -UseDefaultCredentials |
ConvertFrom-JSON | Select data | select -Expand data | select id
Write-Host $projectIds
#obtain all the project ids
ForEach-Object{
[PSObject[]]$pid += $projectIds.id
}
Write-Host $pid
$uri3 = "https://playbook2.com/data/project/export/projects-tasks?projectIds[]="
$getIds = [PSObject[]]$pid -join "&projectIds[]="
$getUri2 = $uri3 + $getIds
$of = "\\ant\dept\DCGSI\Extracts\Time_Tracking_Tasks.xlsx"
Write-Host $getUri2
#retrieve excel files of tasks from each sub-folder
wget $getUri2 -outfile $of -UseDefaultCredentials
}
This code is an adaptation of some other code that I wrote. The 5 other scripts work fine. The main difference is that the other code has to loop through multiple folders and gets the project IDs under each folder, but this code only has to go through a single folder. Now in the other code the $uri3, $getIds code works fine and I get an export. The problem I am seeing in this code is that it isn't joining the URL the way I expect.
https://playbook2.com/data/project/export/projects-tasks?projectIds[]=######&projectIds[]=####### is the expected and previously seen output to get all the project data i need.
The problem with the above script is that it is giving https://playbook2.com/data/project/export/projects-tasks?projectIds[]=&projectIds[]=######&projectIds[]=####### which is invalid.
is there a way that I can tell it to do just $pid for the first item in the object and then -join the "&projectIds[]=" on the next n until the end of the list? I tried
[PSObject[]]$pid | select -Skip 1 -join "&projectIds[]="
and
[PSObject[]]$pid | Select-Object -Skip 1 -join "&projectIds[]="
but that results in nothing being appended.
I found a couple of "mistakes" in your script.
First is that you are using the variable $pid which is an system default variable. You can check the system global variables by typing
Get-Variable
Secondly $pid is defined with an empty string. The correct way to initialize a PSObject is with $myVar = New-Object PSObject. Replace [PSObject[]]$pid = '' with $myProjectIds = New-Object PSObject
For readability I took the liberty to rewrite your script.
function Get-Data(){
$GetProjectsUri = 'https://playbook2.com/data/project/folder/28220'
$ExportProjectsUri = 'https://playbook2.com/data/project/export/projects-tasks?'
$ExportFilePath = "\\ant\dept\DCGSI\Extracts\Time_Tracking_Tasks.xlsx"
$GetProjectsJson = Invoke-WebRequest -Uri $GetProjectsUri -UseDefaultCredentials
Write-Output $GetProjectsJson
$Projects = ConvertFrom-JSON -InputObject $GetProjectsJson
Write-Output $Projects
foreach ($Project in $Projects) {
$ProjectId = $Project.data.id
# Check if ProjectId exists
if ($ProjectId) {
$ExportProjectsUri = $ExportProjectsUri + 'projectIds[]=' + $ProjectId
}
}
Write-Output $ExportProjectsUri
Invoke-WebRequest Invoke-WebRequest -Uri $ExportProjectsUri -outfile $ExportFilePath -UseDefaultCredentials
}
Cheers
Glenn

Procedure to write and execute custom queries for Visual Studio Team Services(VSTS) using Work Item Query Language(wiql)

I wanted to extract data from VSTS using "WIQL" and do some reporting with that data.
1) Could someone please let me know if its is possible to use "WIQL" in PowerShell? If so, please let me know on where i can find the some samples or demos for this?
2) Also, are there any other client tools which support "WIQL" to make custom querying to VSTS. If so, please let me know where i can find some demo or some samples with respect to this?
Yes, it is possible to use WIQL in PowerShell, the easy way is that you could call Work Item Query REST API by using PowerShell.
For example:
$vstsAccount = "XX"
$projectName = "XX"
$user = ""
$token = "personal access token"
Function QueryWorkItem{
# 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.Title] From WorkItems Where [System.Id] = 123'
}"
# Invoke the REST call and capture the results (notice this uses the PATCH method)
$result = Invoke-RestMethod -Uri $uri -Method Post -ContentType "application/json" -Headers #{Authorization=("Basic {0}" -f $base64AuthInfo)} -Body $body
Write-Output ("work item title: '$($result.WorkItems[0].URL)'")
}
QueryWorkItem
More information, you can refer to this article.
For secondly issue, you can build an application by yourself, such as console application with C# language.
Install Microsoft.TeamFoundationServer.ExtendedClient package
Simple C# code
:
var u = new Uri("https://XXX.visualstudio.com");
VssCredentials c = new VssCredentials(new Microsoft.VisualStudio.Services.Common.WindowsCredential(new NetworkCredential("user name", "password")));
var connection = new VssConnection(u, c);
var workitemClient = connection.GetClient<WorkItemTrackingHttpClient>();
var result = workitemClient.QueryByWiqlAsync(new Microsoft.TeamFoundation.WorkItemTracking.WebApi.Models.Wiql() { Query= "Select [System.Id],[System.Title] From WorkItems Where [System.Id] = 123" },"Scrum2015").Result;
Console.WriteLine(result.WorkItems.First().Url);
Update 1:
The TFS/VSTS SDK or extend SDK can be used in PowerShell. For example:
if((Get-PSSnapIn -Name Microsoft.TeamFoundation.PowerShell -ErrorAction SilentlyContinue) -eq $null)
{
Add-PSSnapin Microsoft.TeamFoundation.PowerShell
}
$Tfs2015AssembliesPath="[vs Installation path]\Microsoft Visual Studio 14.0\Common7\IDE\CommonExtensions\Microsoft\TeamFoundation\Team Explorer"
Add-Type -Path "$Tfs2015AssembliesPath\Microsoft.TeamFoundation.Client.dll"
Add-Type -Path "$Tfs2015AssembliesPath\Microsoft.TeamFoundation.Common.dll"
Add-Type -Path "$Tfs2015AssembliesPath\Microsoft.TeamFoundation.Build.Client.dll"
Add-Type -Path "$Tfs2015AssembliesPath\Microsoft.TeamFoundation.Build.Common.dll"
Add-Type -Path "$Tfs2015AssembliesPath\Microsoft.TeamFoundation.Git.Client.dll"
Add-Type -Path "$Tfs2015AssembliesPath\Microsoft.TeamFoundation.SourceControl.WebApi.dll"
#Add-Type -Path "$Tfs2015AssembliesPath\Microsoft.TeamFoundation.TestManagement.Client.dll"
Add-Type -Path "$Tfs2015AssembliesPath\Microsoft.TeamFoundation.VersionControl.Client.dll"
Add-Type -Path "$Tfs2015AssembliesPath\Microsoft.TeamFoundation.WorkItemTracking.Client.dll"
Function GetWorkItems{
param([string]$teamProjectName,[string]$address)
$credentials = New-Object System.Net.NetworkCredential("[user name]", "[password]")
$tfsCollection = New-Object Microsoft.TeamFoundation.Client.TfsTeamProjectCollection((New-Object System.URI($address)))
$wis = $tfsCollection.GetService([Microsoft.TeamFoundation.WorkItemTracking.Client.WorkItemStore])
$wiqlQuery = "SELECT [System.ID], [System.WorkItemType], [System.Title], [System.AssignedTo], [System.State] FROM WorkItems WHERE [System.TeamProject] = 'Scrum2015' AND [State] = 'New' AND [System.WorkItemType] in ('Bug','User Story') ORDER BY [System.ID]";
$witCollection = $wis.Query($wiqlQuery);
# add logical to export to excel.
Foreach($witItem in $witCollection)
{
Write-Host $witItem.Title
}
}
GetWorkItems "[project name]" "[tfs/vsts address]"

Add TFS Tag with REST API via powershell

I want to add a tag to a TFS project using the REST API in Powershell.
I am trying to make this request based on the documentation for
Visual Studio Integration
I am calling this:
[void][System.Reflection.Assembly]::LoadWithPartialName('Microsoft.TeamFoundation.Client')
[void][System.Reflection.Assembly]::LoadWithPartialName('Microsoft.TeamFoundation.WorkItemTracking.Client')
if ( (Get-PSSnapin -Name "Microsoft.TeamFoundation.Powershell" -ErrorAction SilentlyContinue) -eq $null )
{
Add-PSSnapin "Microsoft.TeamFoundation.Powershell"
}
$SrcCollectionUrl = 'http://tfs.myCompany.com:8080/tfs/MyCollection'
$SrcProjectName = 'myProject'
[psobject] $tfs = [Microsoft.TeamFoundation.Client.TeamFoundationServerFactory]::GetServer($SrcCollectionUrl)
[Microsoft.TeamFoundation.WorkItemTracking.Client.WorkItemStoreFlags]$WorkItemBypass = [Microsoft.TeamFoundation.WorkItemTracking.Client.WorkItemStoreFlags]::BypassRules
$tfstp = New-Object Microsoft.TeamFoundation.Client.TfsTeamProjectCollection($SrcCollectionUrl)
$WorkItemStore = New-Object -TypeName 'Microsoft.TeamFoundation.WorkItemTracking.Client.WorkItemStore' -ArgumentList $tfs.TfsTeamProjectCollection, $WorkItemBypass
$SrcProject = $WorkItemStore.Projects[$SrcProjectName]
$ProjectGuid = Split-Path $SrcProject.Uri -Leaf
$AddTagsUrl = '{0}/_apis/tagging/scopes/{1}/tags?api-version=1.0' -f $SrcCollectionUrl,$ProjectGuid
$newTagParams = #{name="PWCreateTag2"}
$outjson = $newTagParams | ConvertTo-Json
$nresp = Invoke-RestMethod -Method POST -Uri $AddTagsUrl -UseDefaultCredentials -Body $outjson -ContentType 'application/json'
Everything works. The first time. However the documentation states: "If a tag by that name already exists, no tag is created. Instead, the response body includes the existing tag with that name."
The 2nd time I call the line I get: "The remote server returned an error: (400) Bad Request."
Anyone have any Idea why this fails the 2nd time?
FYI: TFS Server is 2015, Powershell is V4
I created powershell module for this - tfs
To add tags:
'tag1','tag2' | % { Add-TFSBuildTag -Id 520 -Tag $_ }