I have an azure build pipeline task that publishes/creats wiki from a repository. The code is the following
- task: AzurePowerShell#5
inputs:
azureSubscription: '$(service_connection)'
ScriptType: 'InlineScript'
Inline: |
$url = 'https://dev.azure.com/data-platform/uc_test/_apis/wiki/wikis?api-version=6.0'
$body= #'
{
"version": {
"version": "main"
},
"type": "codeWiki",
"name": "wiki",
"projectId": "c800c392-67aa-4892-a070-7284435bda9b",
"repositoryId": "2avd6061-b265-417f-8b55-81cc65kf90b6",
"mappedPath": "/"
}
'#
$response = Invoke-RestMethod -Uri $url -Headers #{Authorization = "Bearer $(System.AccessToken)"} -Method Post -Body $body -ContentType application/json
echo $response
azurePowerShellVersion: 'LatestVersion'
On the first run of this build pipeline, it successfully publishes/creates wiki. But in the next run, it throws the following error
{"$id":"1","innerException":null,"message":"Wiki already exists with
| name
| 'wiki'.","typeName":"Microsoft.TeamFoundation.Wiki.Server.WikiAlreadyExistsException, Microsoft.TeamFoundation.Wiki.Server","typeKey":"WikiAlreadyExistsException","errorCode":0,"eventId":3000}
How can I tackle this issue? I was thinking if there any way to implement a if else condition under AzurePowerShell#5 task - if wiki doesn't exist- create wiki, else skip the script.
Wiki already exists with
The cause of the issue is that wiki names are unique. When you run successfully for the first time, the wiki name already exists. You need to change the wiki name to let it work again.
I was thinking if there any way to implement a if else condition under AzurePowerShell#5 task - if wiki doesn't exist- create wiki, else skip the script.
Yes. You can use PowerShell script to run the Rest API: Wikis - List to list all wiki names. Then we can check if the wiki name exists and do next actions.
Here is an example:
$Testwikiname = "sampleCodeWiki10"
$url="https://dev.azure.com/orgname/projectname/_apis/wiki/wikis?api-version=7.0"
$token = [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes(":$(system.accesstoken)"))
$response = Invoke-RestMethod -Uri $url -Headers #{Authorization = "Basic $token"} -Method Get -ContentType application/json
echo $response
$count = 0
Foreach($wikiname in $response.value.name)
{
echo $wikiname
if($wikiname -eq $Testwikiname)
{
$count = $count + 1
}
}
echo $count
if($count -eq 1 )
{
echo "Wiki Page: $Testwikiname exists in the project"
}
else
{
$url1="https://dev.azure.com/orgname/project/_apis/wiki/wikis?api-version=6.0"
$JSON =
"{
`"version`": {
`"version`": `"master`"
},
`"type`": `"codeWiki`",
`"name`": `"$Testwikiname`",
`"projectId`": `"aaea0fe6-801c-46e3-be1e-dcacdcb0c384`",
`"repositoryId`": `"d7c2d91d-9175-4a45-97bc-aa743275bebc`",
`"mappedPath`": `"/test5`"
}"
$response1 = Invoke-RestMethod -Uri $url1 -Headers #{Authorization = "Basic $token"} -Method Post -Body $JSON -ContentType application/json
echo " Wiki Page: $Testwikiname has been created"
}
Result:
Your task works as expected.... Before creation, you have to check the existence of your wiki... Check this request: Query by name
Related
I have powershell script which is creating annotated tag for commit in azure repos, but when I run this script in azure release pipeline I got error: "The combination of parameters is either not valid or not complete." When I make call from Postman with my credentials. its working. I also set repo permission Create Tag to Allow. Which permission I need for creating annotated tags with build user?
$createTagUrl= "$($env:SYSTEM_TEAMFOUNDATIONCOLLECTIONURI)$env:SYSTEM_TEAMPROJECT/_apis/git/repositories/$($env:BUILD_REPOSITORY_ID)/annotatedtags?api-version=6.0-preview.1"
Write-Host "createTagUrl=" $createTagUrl
$jsonObject = #"
{
"name": "$($env:RELEASE_RELEASENAME)-$(ENVIRONMENT)",
"taggedObject":{
"objectId": "$($env:BUILD_BUILDID)"
},
"message": "test"
}
"#
Write-Host "JsonObject: " $jsonObject
$json = #($jsonObject) | ConvertTo-Json -Depth 99
$createdTag = Invoke-RestMethod -Uri $createTagUrl -Method Post -Body $json -ContentType "application/json" -Headers #{Authorization = "Bearer $env:SYSTEM_ACCESSTOKEN"}
I can reproduce your issue:
The root cause is the code on your side is wrong.
This should works:
$headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
$headers.Add("Authorization", "Basic <PAT>")
$headers.Add("Content-Type", "application/json")
$body = "{
`n `"name`": `"xxx`",
`n `"message`": `"xxx`",
`n `"taggedObject`": {
`n `"objectId`": `"<object id>`"
`n }
`n}"
$response = Invoke-RestMethod 'https://dev.azure.com/<Organization Name>/<Project Name>/_apis/git/repositories/<Repo Name>/annotatedtags?api-version=4.1-preview.1' -Method 'POST' -Headers $headers -Body $body
$response | ConvertTo-Json
I'm trying to add Iterations and Calendar holidays to my Azure DevOps project and can't figure it out.
Using Azure DevOps CLI and some PowerShell script code found here then running it, the window immediately closes. Nothing is created in any project and no console writing happens:
Param
(
[string]$PAT = [my PAT],
[string]$Organization = [my organization],
[string]$Project = 'DevTeam',
[string]$TeamName = 'DevTeam Team',
[DateTime]$StartDate = Get-Date,
[int]$NumberOfSprints = 18
)
echo $PAT | az devops login --org $Organization
Write-Host '===Configuring connection to organization and Team Project'
az devops configure --defaults organization=$Organization project=$Project
For ($i=1; $i -le $NumberOfSprints; $i++)
{
$StartDateIteration = $StartDate.AddDays(($i - 1) * 14)
$FinishDateIteration = $StartDateIteration.AddDays(20)
$Sprint = $StartDateIteration + '-' + $FinishDateIteration
$createIteration = az boards iteration project create --name $Sprint --start-date $StartDateIteration --finish-date $FinishDateIteration --org $Organization --project $Project | ConvertFrom-Json
$addIteration = az boards iteration team add --id $createIteration.Identifier --team $TeamName --org $Organization --project $Project | ConvertFrom-Json
Write-Host $addIteration.name 'created on path'$addIteration.path
}
So I found this that uses the REST API. I wrote something quickly in Postman for the REST API, but that is also returning an error:
Pre-request script:
var occurences = 18;
var start_date = "11/11/2020";
for(i=1; i <= occurences; i++){
var repeat_every = 21*i; //repeat every number of days/weeks/months
var first = new Date(start_date);
first.setDate( first.getDate() + repeat_every );
var last = new Date(first.setDate(first.getDate() - 1));
var name = first.toDateString() + "-" + last.toDateString();
pm.environment.set("first", first);
pm.environment.set("last", last);
pm.environment.set("name", name);
};
JSON POST body to "dev.azure.com/[my organization]/DevTeam/_apis/work/teamsettings/iterations?api-version=6.0"
{
"id": [my identifier from "GET dev.azure.com/[my organization]/DevTeam/_apis/wit/classificationnodes/iterations?api-version=6.0],
"name": {{name}},
"path": "DevTeam\\Iteration",
"attributes": {
"startDate": {{first}},
"finishDate": {{last}}
}
}
Error:
{
"$id": "1",
"innerException": null,
"message": "TF400898: An Internal Error Occurred. Activity Id: bbe88905-2083-47e2-9b2c-d87be87c4adb.",
"typeName": "Newtonsoft.Json.JsonReaderException, Newtonsoft.Json",
"typeKey": "JsonReaderException",
"errorCode": 0,
"eventId": 0
}
How can I make either of these run successfully?
Will this automatically add iterations on the Calendar? The idea is to consolidate calendars and have DevOps track our work hours and time off so we can use that in sprint planning.
Thanks.
I would like to share the Rest API method.
This Rest API : Iterations - Post Team Iteration is only used to add the Iteration to team.
If you need to create or change an existing iteration, you need to combine the following two APIs:
Create New Iteration: Classification Nodes - Create Or Update
Update Existing Iteration: Classification Nodes - Update
Here are the PowerShell script examples:
Create new iteration and add the iteration to the team:
$token = "PAT"
$url="https://dev.azure.com/{OrganizationName}/{ProjectName}/_apis/wit/classificationnodes/Iterations?api-version=5.0"
$token = [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes(":$($token)"))
$JSON = #'
{
"name": "kevin5 Iteration",
"attributes": {
"startDate": "2014-10-27T00:00:00Z",
"finishDate": "2014-10-31T00:00:00Z"
}
}
'#
$response = Invoke-RestMethod -Uri $url -Headers #{Authorization = "Basic $token"} -Method Post -Body $JSON -ContentType application/json
$IterationId = $response.identifier
echo $IterationId
$url2 = "https://dev.azure.com/{OrganizationName}/{ProjectName}/{TeamName}/_apis/work/teamsettings/iterations?api-version=6.0"
$body = "{
`"id`": `"$IterationId`"
}"
$response = Invoke-RestMethod -Uri $url2 -Headers #{Authorization = "Basic $token"} -Method Post -Body $body -ContentType application/json
Update the existing Iteration and add the iteration to the team:
$token = "PAT"
$url="https://dev.azure.com/{OrganizationName}/{ProjectName}/_apis/wit/classificationnodes/iterations/{IterationName}?api-version=5.0"
$token = [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes(":$($token)"))
$JSON = #'
{
"name": "Edit Iteration",
"attributes": {
"startDate": "2014-10-27T00:00:00Z",
"finishDate": "2014-10-31T00:00:00Z"
}
}
'#
$response = Invoke-RestMethod -Uri $url -Headers #{Authorization = "Basic $token"} -Method PATCH -Body $JSON -ContentType application/json
$IterationId = $response.identifier
echo $IterationId
$url2 = "https://dev.azure.com/{OrganizationName}/{ProjectName}/{TeamName}/_apis/work/teamsettings/iterations?api-version=6.0"
$body = "{
`"id`": `"$IterationId`"
}"
$response = Invoke-RestMethod -Uri $url2 -Headers #{Authorization = "Basic $token"} -Method Post -Body $body -ContentType application/json
I have a custom build step that fails under certain conditions during my Pull Request build, within Azure DevOps.
I would like to extend it further by raising a PR comment, similar to this sort of thing in GitHub:
https://developer.github.com/v3/issues/comments/#create-a-comment
I don't have code samples to add here as I could not find useful examples to build upon. I use PowerShell for my custom build step - how do I achieve this when running a PR build of my branch?
I can help with an example. There is a bunch of value in posting custom messages\status to PRs from your pipelines.
First things first, make sure your build service has permissions to contribute to pull requests in your repository.
Then you want to add a conditional PowerShell step. This one is just based on it being a PR build, but you might want to add a depends on failure for the previous step, based on your workflow.
- task: PowerShell#2
condition: eq(variables['Build.Reason'], 'PullRequest')
displayName: Post Message to PR
env:
SYSTEM_ACCESSTOKEN: $(System.AccessToken)
inputs:
targetType: filePath
filePath: PostToPR.ps1
So the basic workflow is:
Build the Markdown Message
Build the JSON Body
Post the Message to the PR
PostToPR.ps1
#Going to create the comment in an Active state, assuming it needs to be resolved
#See https://learn.microsoft.com/en-us/dotnet/api/microsoft.teamfoundation.sourcecontrol.webapi.commentthreadstatus?view=azure-devops-dotnet
$StatusCode = 1
$Stuff = $env:Build_Repository_Name
$Things = "Other things you might want in the message"
#Build Up a Markdown Message to
$Markdown = #"
## Markdown Message here
|Column0 |Column1|
|--------|---------|
|$Stuff|$Things|
"#
#Build the JSON body up
$body = #"
{
"comments": [
{
"parentCommentId": 0,
"content": "$Markdown",
"commentType": 1
}
],
"status": $StatusCode
}
"#
Write-Debug $Body
#Post the message to the Pull Request
#https://learn.microsoft.com/en-us/rest/api/azure/devops/git/pull%20request%20threads?view=azure-devops-rest-5.1
try {
$url = "$($env:SYSTEM_TEAMFOUNDATIONCOLLECTIONURI)$env:SYSTEM_TEAMPROJECTID/_apis/git/repositories/$($env:Build_Repository_Name)/pullRequests/$($env:System_PullRequest_PullRequestId)/threads?api-version=5.1"
Write-Host "URL: $url"
$response = Invoke-RestMethod -Uri $url -Method POST -Headers #{Authorization = "Bearer $env:SYSTEM_ACCESSTOKEN"} -Body $Body -ContentType application/json
if ($response -ne $Null) {
Write-Host "*******************Bingo*********************************"
}
}
catch {
Write-Error $_
Write-Error $_.Exception.Message
}
And you end up with a nice markdown table with custom status information in your PR!
Building on the great answers already provided, this is the equivalent inline YAML pipeline script:
- powershell: |
$body = #"
{
"comments": [
{
"parentCommentId": 0,
"content": "Your comment here",
"commentType": 1
}
],
"status": 4
}
"#
$url = "$($env:SYSTEM_TEAMFOUNDATIONCOLLECTIONURI)$env:SYSTEM_TEAMPROJECTID/_apis/git/repositories/$($env:Build_Repository_Name)/pullRequests/$($env:System_PullRequest_PullRequestId)/threads?api-version=5.1"
$result = Invoke-RestMethod -Uri $url -Method POST -Headers #{Authorization = "Bearer $(System.AccessToken)"} -Body $Body -ContentType application/json
displayName: Post comment on PR
If you mean creating PR comment in the build pipeline, then you can add a PowerShell task in your pipeline and run the script to call the REST API (Pull Request Thread Comments - Create).
Below PowerShell script for your reference:
Param(
[string]$baseurl = "https://dev.azure.com/{organization}",
[string]$projectName = "0508-t",
[string]$repositoryId = "62c8ce54-a7bb-4e08-8ed7-40b27831bd8b",
[string]$pullRequestId = "35",
[string]$threadId = "229",
[string]$user = "",
[string]$token = "PAT"
)
# Base64-encodes the Personal Access Token (PAT) appropriately
$base64AuthInfo = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(("{0}:{1}" -f $user,$token)))
write-host $WorkitemType
#Create Jason body
function CreateJsonBody
{
$value = #"
{
"content": "Test Comment 0204",
"parentCommentId": 1,
"commentType": 1
}
"#
return $value
}
$json = CreateJsonBody
$uri = "$baseurl/$projectName/_apis/git/repositories/$repositoryId/pullRequests/$pullRequestId/threads/$threadId/comments?api-version=5.1"
Write-Host $uri
$result = Invoke-RestMethod -Uri $uri -Method Post -Body $json -ContentType "application/json" -Headers #{Authorization=("Basic {0}" -f $base64AuthInfo)}
I am able to update the variable in the build pipeline using the below json body
$body = '
{
"definition": {
"id": 25
},
"parameters": "{\"var\":\"value\"}"
}
'
The same json is not working with Release pipeline . Is there any way to pass the variable through same way through release pipeline
Set Azure devops Release pipeline variable using REST API
We could use the REST API Definitions - Get to get all the info about this definition in the body, then we could update the body and use the (Definitions - Update) to update the value of the release definition variable from a release pipeline:
PUT https://vsrm.dev.azure.com/{organization}/{project}/_apis/release/definitions/{definitionId}?api-version=5.0
Following is my test inline powershell scripts:
$url = "https://vsrm.dev.azure.com/{organization}/{project}/_apis/release/definitions/{definitionId}?api-version=5.1"
Write-Host "URL: $url"
$pipeline = Invoke-RestMethod -Uri $url -Method Get -Headers #{
Authorization = "Bearer $env:SYSTEM_ACCESSTOKEN"
}
Write-Host "Pipeline = $($pipeline | ConvertTo-Json -Depth 100)"
# Update an existing variable named TestVar to its new value 2
$pipeline.variables.TestVar.value = "789"
####****************** update the modified object **************************
$json = #($pipeline) | ConvertTo-Json -Depth 99
$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 'TestVar' is updated to" $updatedef.variables.TestVar.value
As test result, the variable TestVar updated to 789:
Update:
But I want to achieve it without updating\changing the definition
The answer is yes. You could use the Releases - Create with request body:
{
"definitionId": Id,
"environments": [
{
"variables": {
"TestVar": {
"value": "xxxx"
},
"TestVar2": {
"value": "xxxx"
}
},
}
],
}
For more information refer the post here.
Hope this helps.
Old topic, but there is a better way now and I believe it deserves a new answer (maybe it was even available since the very beginning, don't know.)
Instead of updating the very definition of the pipeline which only works for future releases, you can now update the currently running release only and that solves your problem.
This is how I set up the tasks in the pipeline:
And here's a snippet from the Powershell task:
(it updates delay_minutes release variable based on deploy_time variable which specifies time in HH:mm format)
if(!"$(deploy_time)") {
Write-Host "deploy_time empty, won't delay deployment"
return
}
$url = "$(System.TeamFoundationServerUri)/$(System.TeamProjectId)/_apis/release/releases/$(Release.ReleaseId)?api-version=5.0"
# Uncomment for debugging
# Write-Host "URL: $url"
$delayMinutes = [int](New-TimeSpan -start (Get-Date) -end "$(deploy_time)").TotalMinutes
if($delayMinutes -lt 0) { $delayMinutes = 0 }
$pipeline = Invoke-RestMethod -Uri $url -Method Get -Headers #{
Authorization = "Bearer $env:SYSTEM_ACCESSTOKEN"
}
# Uncomment for debugging
# Write-Host "Pipeline = $($pipeline | ConvertTo-Json -Depth 100)"
$pipeline.variables.delay_minutes.value = $delayMinutes
$json = #($pipeline) | ConvertTo-Json -Depth 99
$updatedef = Invoke-RestMethod -Uri $url -Method Put -Body $json -ContentType "application/json" -Headers #{Authorization = "Bearer $env:SYSTEM_ACCESSTOKEN"}
The URL in the snippet uses only always available predefined variables so it should be 100% copy-pastable.
Also make sure to set this on the first agent job:
So that the SYSTEM_TOKEN variable is available in the script.
As part of our release pipeline we have a task (the last task) to merge the release branch back to master.
I was wondering whether there is a way to check that this task or the previous release has completed before allowing the new release to be queued. Can a gate be used for this?
Ideally, the release manager would then be able to decide whether they want to continue with the release or to cancel.
You can't use the Invoke Rest API gate with an Azure DevOps API url because for checking the last release status you need to check the environment (stage) status, and for this, you need to the release id (so you can't know what it will be and put it in the rest API gate URL).
But, you can use PowerShell to check the last release and if it is not succeeded just fail the stage.
Add a PowerShell task in your release to check the last release:
$headers = #{ Authorization = "Bearer $env:SYSTEM_ACCESSTOKEN" }
# Replace {org} with your organization
# Replace {project} with your project
# Replace {defId} with your release definition id
$url = "https://vsrm.dev.azure.com/{org}/{project}/_apis/release/releases?definitionId={defId}&api-version=5.1"
$releases = Invoke-RestMethod -Method Get -Uri $url -Headers $headers -ContentType 'application/json'
$releaseUrl = "https://vsrm.dev.azure.com/{org}/{project}/_apis/release/releases/$($releases.value[1].id)?api-version=5.1"
$releaseInfo = Invoke-RestMethod -Method Get -Uri $releaseUrl -Headers $headers -ContentType 'application/json'
$releaseEvnriomentId = $releaseInfo.environments.Where({ $_.name -eq 'THE STAGE NAME WHERE YOU DO MERGE' }).id
$envUrl = "https://vsrm.dev.azure.com/{org}/{project}/_apis/Release/releases/$($releases.value[1].id)/environments/$($releaseEvnriomentId)?api-version=5.1-preview.1"
$environment = Invoke-RestMethod -Method Get -Uri $envUrl -Headers $headers -ContentType 'application/json'
$envStatus = $environment.status
if($envStatus -ne "succeeded")
{
Write-Error "Previous release not succeeded!"
}
else
{
Write-Host "Previous release succeeded :)"
}
In the agent job options you need to allow scripts to access the OAuth token:
Azure functions also support PowerShell so you do it also with Azure functions gate:
1) Create a new Azure Function with VS Code like explained here.
2) In your run.ps1 file replace the code to this code:
using namespace System.Net
# Input bindings are passed in via param block.
param($Request, $TriggerMetadata)
# Write to the Azure Functions log stream.
Write-Host "PowerShell HTTP trigger function processed a request."
$defnitionId = $Request.Query.DefinitionId
# Generate PAT and put it in the {YOUR PAT}
$base64AuthInfo = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(("{0}:{1}" -f $user,"{YOUR PAT}")))
$headers = #{Authorization=("Basic {0}" -f $base64AuthInfo)}
# Replace {org} with your organization
# Replace {project} with your project
$url = "https://vsrm.dev.azure.com/{org}/{project}/_apis/release/releases?definitionId=$($defnitionId)&api-version=5.1"
$releases = Invoke-RestMethod -Method Get -Uri $url -Headers $headers -ContentType 'application/json'
Write-Debug $releases
$releaseUrl = "https://vsrm.dev.azure.com/{org}/{project}/_apis/release/releases/$($releases.value[1].id)?api-version=5.1"
$releaseInfo = Invoke-RestMethod -Method Get -Uri $releaseUrl -Headers $headers -ContentType 'application/json'
Write-Debug $releaseInfo
$releaseEvnriomentId = $releaseInfo.environments.Where({ $_.name -eq 'THE STAGE NAME WHERE YOU DO MERGE' }).id
$envUrl = "https://vsrm.dev.azure.com/{org}/{project}/_apis/Release/releases/$($releases.value[1].id)/environments/$($releaseEvnriomentId)?api-version=5.1-preview.1"
$environment = Invoke-RestMethod -Method Get -Uri $envUrl -Headers $headers -ContentType 'application/json'
Write-Debug $environment
$envStatus = $environment.status
Write-Debug $envStatus
if($envStatus -ne "succeeded")
{
$status = [HttpStatusCode]::BadRequest
$body = "failed"
}
else
{
$status = [HttpStatusCode]::OK
$body = "success"
}
# Associate values to output bindings by calling 'Push-OutputBinding'.
Push-OutputBinding -Name Response -Value ([HttpResponseContext]#{
StatusCode = $status
Body = $body
})
3) Publish the function to Azure.
4) Create an Invoke Azure Function gate in your release:
Another option, take the above code, convert him to C# or another language ans use Rest API, deploy to it web server and use the Invoke Rest API gate.