Search notes:
TFS Web API
Some simple investigations on using the
TFS Web API with
PowerShell.
Function to request a
JSON object from the
WebAPI endpoint:
function get-webApiJson {
param (
[string] $url
)
$res = invoke-webRequest -useDefaultCredential $url
convertFrom-json $res.Content
}
Define some global variables that specify a TFS collection's WebAPI
URL:
$rootUrl = 'https://devops.foo.xyz/'
$collection = 'DefaultCollection'
$collUrl = "$rootUrl$collection"
Iterate over available projects
$projects = get-webApiJson "$collUrl/_apis/projects"
write-host ''
foreach ($project in $projects.value) {
write-host "$($project.name): $($project.description)"
write-host " id: $($project.id)"
write-host " url: $($project.url)"
write-host " revision: $($project.revision) ($($project.lastUpdateTime))"
write-host ''
}
Choose a project URL and ID from output shown above:
$project_id = '01234567-89ab-cdef-0123-456789abcdef'
$project_url = "$collUrl/_apis/projects/$project_id"
$projectDetail = get-webApiJson $project_url
$defaultTeam = $projectDetail.defaultTeam
write-host "Default Team name: $($defaultTeam.name)"
$releases = get-webApiJson "$collUrl/_apis/release/releases"
write-host "Number of releases: $($releases.count)"
$lastRelease = $releases.value[$releases.count-1]
$releaseModifier = $lastRelease.modifiedBy
write-host "Last release id : $($lastRelease.id)"
write-host "Last release name : $($lastRelease.name)"
write-host "Last relase modified by : $($releaseModifier.displayName) ($($releaseModifier.uniqueName)), url = $($releaseModifier.url)"
$releaseDetail = get-webApiJson $lastRelease.url
$processes = get-webApiJson "$collUrl/_apis/process/processes"
write-host "count of processes: $($processes.count)"
write-host "first process: $($processes.value[0].description)"
$repoRoot = get-webApiJson "$collUrl/_apis/tfvc/items?scopePath=$/&recursionLevel=oneLevel"
$repoRoot
List items in a TFS directory
A simple function to list the items in a TFS directory:
function ls-TFSDirectory {
param (
[string] $urlRoot,
[string] $path
)
$dir = get-webApiJson "$urlRoot/_apis/tfvc/items?scopePath=$path&recursionLevel=oneLevel"
foreach ($item in $dir.value) {
if ($item.psobject.Properties.name -match 'isFolder') {
$dir_file = 'd'
ts
$size = ''
}
else {
$dir_file = 'f'
$size = $item.size
}
write-host ( '{0} {1,6} {2,4} {3,-40}' -f $dir_file, $size, $item.version, $item.path)
}
}
Using the function:
ls-TFSDirectory $collUrl $/path/to/directory
Show change sets
Function to render a change set in a readable form:
function format-changeSet ($v) {
$comment = ''
if ($v.PSObject.properties.item("comment") -ne $null) { $comment = $v.comment}
[dateTime] $createDt = $v.createdDate
'{0,5} {1,-15} {2,-30} {3}' -f $v.changeSetId, ($createDt.ToString('yyyy-MM-dd HH:mm')), $v.author.displayName, $comment
}
Iterate over the change sets that affected a given file:
$path='$/PROJECT/path/to/file.txt'
$versions=(get-webApiJson "$collUrl/_apis/tfvc/changesets?searchCriteria.itemPath=$path").value
foreach ($v in $versions){
format-changeSet $v
}
Iterate over the last n
changesets that were checked in by given person under a given path:
#
# https://docs.microsoft.com/en-us/rest/api/azure/devops/tfvc/changesets/get-changesets?view=azure-devops-rest-6.1
#
$top_n = 100
$path = '$/TRAUM/DWH'
$author = 'Nyffenegger, René'
foreach ($changeSet in (get-webApiJson ("$collUrl/_apis/tfvc/changesets?project=$project_id" +
"&`$top=$top_n" +
"&`searchCriteria.itemPath=$path" +
"&`searchCriteria.author=$author"
)).value) {
format-changeset $changeSet
}
Pipelines
List available pipelines
foreach ($pipeline in (get-webApiJson "$collUrl/$project_id/_apis/pipelines").value ) {
'{0,3}: {1,-40} {2,-30} {3}' -f $pipeline.id, $pipeline.name, $pipeline.folder, $pipeline.url
}
The first column printed is the pipeline's id. It might be stored in a
variable to inspect details of a pipeline:
$pipeline_id = 48
Store the data of an entire pipeline in a variable:
$pipeline = get-webApiJson "$collUrl/$project_id/_apis/pipelines/$pipeline_id"
Show variables of a pipeline
$pipeline_variables = $pipeline.configuration.designerJson.variables
foreach ($var in $pipeline_variables | get-member -type noteProperty) {
$var_name = $var.name
'{0,-30}: {1,-90}' -f $var_name, $pipeline_variables.$var_name.value # , ($pipeline_variables.$var_name.allowOverride)
}
Run a pipeline
The following POST request runs the pipeline with ID $pipeline_id
and assigns the value fooBarBaz
to the variable VersionFileName
:
invoke-webRequest `
-useDefaultCredentials `
-method post `
-headers @{ 'Accept' = 'application/json' ;
'Content-Type' = 'application/json' } `
"$collUrl/$project_id/_apis/pipelines/$pipeline_id/runs?api-version=6.0-preview" `
-body '{
"variables": {
"VersionFileName": {
"secret": false,
"value": "fooBarBaz"
}
},
}'
Show runs of a pipeline
foreach ($run in (get-webApiJson "$collUrl/$project_id/_apis/pipelines/$pipeline_id/runs").value) {
$finishedDate = $run.psObject.members.item('finishedDate')
if ($finishedDate -ne $null) {
$finishedDate = ([datetime] $run.finishedDate).ToLocalTime().ToString('yyyy-MM-dd HH:mm:ss')
}
else {
$finishedDate = '-'
}
$result = $run.psObject.members.item('result')
if ($result -ne $null) {
$result = $run.result
}
else {
$result = '-'
}
'{0}: {1:yyyy-MM-dd HH:mm:ss} {2,-19} {3,-60} {4,-10} {5,-10}' -f `
$run.id,
(([datetime] $run.createdDate ).ToLocalTime()),
$finishedDate,
$run.name,
$run.state,
$result
}
Builds
List builds
List the 20 most recent builds ($top=…
)
$top = 20
foreach ($build in (get-webApiJson "$collUrl/$project_id/_apis/build/builds?`$top=$top").value) {
#
# result is empty if status is 'In Progress'
#
$result = $build.psObject.members.item('result')
if ($result -ne $null) {
$result = $result.value
}
else {
$result = 'n/a'
}
'{0,5} | {1:yyyy-MM-dd HH:mm:ss} | {2,-40} {3,-30} {4,-11} {5,-11}' -f `
$build.id,
([datetime] $build.queueTime).ToLocalTime(),
$build.buildNumber,
$build.requestedFor.displayName,
$build.status,
$result
}
Show details about a build with a given id
$build_id = 7141
$bld_det = get-webApiJson "$collUrl/$project_id/_apis/build/builds/$build_id"
write-host "Build Rev Nr. $($bld_det.buildNumberRevision)"
write-host "Source version: $($bld_det.sourceVersion)"
write-host "Queue: $($bld_det.queue.name) | Pool: $($bld_det.queue.pool.id) $($bld_det.queue.pool.name)"
write-host "Definition id: $($bld_det.definition.id)"
write-host " url: $($bld_det.definition.url)"
write-host "Logs: $($bld_det.logs.url)"
write-host "Parameters:"
$params_ = $bld_det.parameters |convertFrom-json
foreach ($param in ($params_ | get-member -type noteProperty)) {
write-host " $($param.name): $($params_.($param.name))"
}
write-host "orchestrationPlan: $($bld_det.orchestrationPlan.planid)"
Build definitions
foreach ($def in (get-webApiJson "$collUrl/$project_id/_apis/build/definitions").value) {
write-host ( '{0,3}: {1,-50} {2,-8} | {3,-2} {4,-20} | {5,-10}' -f `
$def.id,
$def.name,
$def.type, # build ...?
$def.queue.id,
$def.queue.name,
$def.queueStatus # enabled, paused, ...?
)
}
Releases
Show definitions
foreach ($rel in (get-webApiJson "$collUrl/$project_id/_apis/release/definitions").value) {
'{0,3}: {1,-60} {2,-20} {3}' -f $rel.id, $rel.name, $rel.path, $rel.releaseNameFormat
}
$release_definition_id = 18
Print details of a release definition
function print-release-definition {
param (
[int] $rel_def_id
)
$rel_def = get-webApiJson "$collUrl/$project_id/_apis/release/definitions/$rel_def_id"
#
write-host "id : $($rel_def.id)"
#
write-host "Last release: $( ([dateTime] $rel_def.lastRelease.createdOn).ToLocalTime().ToString('yyyy-MM-dd HH:mm:ss')) ($($rel_def.lastRelease.id))"
# write-host "Last release"
#
write-host "Environments"
foreach ($env in $rel_def.environments) {
write-host (' {0,3}: {1}' -f $env.id, $env.name )
write-host (" Current release: $($env.currentRelease.id) [$($env.currentRelease.url)]")
write-host (" Deploy step : $($env.deployStep.id)")
write-host (" Badge URL : $($env.badgeUrl)")
#
write-host (" deploy phases : $($env.deployPhases)")
#
write-host
}
#
write-host "Artifacts"
foreach ($art in $rel_def.artifacts) {
write-host (" $($art.sourceId)")
write-host (" $($art.type)")
write-host (" $($art.alias)")
write-host (" $($art.isPrimary)")
#
write-host
}
}
print-release-definition $release_definition_id
Show «executed» releases
foreach ($rel in (get-webApiJson "$collUrl/$project_id/_apis/release/releases").value) {
'{0,4}: {1,-60} {2,-7} {3:yyyy-MM-dd HH:mm:ss} {4,3} {5,-30} {6,-20}' -f `
$rel.id,
$rel.name,
$rel.status,
( ([datetime] $rel.createdOn).ToLocalTime()),
$rel.releaseDefinition.id,
$rel.releaseDefinition.name,
$rel.createdBy.displayName
}
Show details of a given release
function print-release-details {
param (
[int] $rel_id
)
$rel_det = get-webApiJson "$collUrl/$project_id/_apis/release/releases/$rel_id"
write-host "Release Details for $($rel_det.name) ($($rel_det.id))"
write-host "Log container URL: $($rel_det.logsContainerUrl)"
write-host "URL: $($rel_det.url)"
foreach ($env in $rel_det.environments) {
write-host (' {0,3}: {1}' -f $env.id, $env.name )
write-host (" Status : $($env.status)")
write-host (" Rank : $($env.rank)")
write-host (" Release : $($env.release.id)")
write-host (" Release definition : $($env.releaseDefinition.id)")
write-host (" Deploy Steps")
foreach ($step in $env.deploySteps) {
write-host (" $($step.id) - $($step.status)")
foreach ($phase in $step.releaseDeployPhases) {
write-host (" phase: $($phase.id)")
write-host (" runPlandId: $($phase.runPlanId)")
}
}
}
}
Download and compare versioned files
Function to download a file from a web service:
function download-file {
param (
[string] $url,
[string] $localFile
)
$res = invoke-webRequest -useDefaultCredential $url
[System.IO.File]::WriteAllBytes($localFile, $res.Content)
}
This function uses download-file
to download a file that is versioned in TFVC:
function download-tfvcFile {
param (
[string] $path,
[int] $version
)
$leaf = split-path -leaf $path
$filename = "$pwd/$version.$leaf"
download-file "$collUrl/_apis/tfvc/items?path=$path&versionDescriptor.version=$version" $filename
return $filename
}
function compare-tfvcExcelSheets {
param (
[string] $path,
[int] $version_1,
[int] $version_2
)
$filename_1 = download-tfvcFile $path $version_1
$filename_2 = download-tfvcFile $path $version_2
compare-spreadsheets $filename_1 $filename_2
}
compare-tfvcExcelSheets $path 15310 15656