I am trying to automate the creation of release notes in Azure DevOps release pipelines by creating a new page in the Azure DevOps wiki using it's Rest API.
The issue I'm having is that I'm using a PowerShell script task to post to the Rest API and I'd like to avoid using a Personal Access Token (PAT) and use OAuth instead. The PAT expires and I don't want all of the releases to suddenly fail when the PAT expires. By running the PowerShell script using OAuth under the context of the build agent, I can avoid this expiration issue. I have the "Allow scripts to access the OAuth token" checked in the Agent Job for the release where the PowerShell script task is.
To keep things simple, I'm running the following inline PowerShell script to test creating a new wiki page using OAuth ("Bearer $env:SYSTEM_ACCESSTOKEN"
):
$uri = "https://dev.azure.com/{organization}/{project}/_apis/wiki/wikis/{wikiIdentifier}/pages?api-version=5.0&path=/Release%20Notes/Customers%20API/Release-299%20[Build:%2020191010.1]";
try {
$response = Invoke-RestMethod `
-Method PUT $uri `
-Headers @{Authorization = "Bearer $env:SYSTEM_ACCESSTOKEN"} `
-ContentType "application/json" `
-Body $json
} catch {
Write-Host "Message: " $_.Exception.Message;
Write-Host "StatusCode: " $_.Exception.Response.StatusCode.value__ ;
Write-Host "StatusDescription: " $_.Exception.Response.StatusDescription;
}
If I perform the HTTP PUT to the URL above using Postman and a Personal Access Token, the new wiki page is successfully created. However, if I run the above PowerShell script within the context of a release in Azure DevOps, I get an HTTP 400 "Bad Request" response. I find this odd since a similar PowerShell script task can query the wiki's Rest API for the existence of pages without error.
Is it not possible to create a new Wiki page using a Bearer token (OAuth) via the wiki's Rest API or am I doing something wrong?
Is it not possible to create a new Wiki page using a Bearer token (OAuth) via the wiki's Rest API?
Of course yes, it is possible. Any rest api that you can execute with PAT token locally, are all can be run programmatically with System.AccessToken
in Powershell task.
For the error you are facing, in fact, if you just Write-Host $response
, the error message would make you clearer:
Invoke-RestMethod : {"$id":"1","innerException":null,"message":"The wiki page operation failed with message : User does not have write permissions for this wiki.","typeName":"Microsoft.TeamFoundation.Wiki.Server.WikiPageOperationFailedException,
Microsoft.TeamFoundation.Wiki.Server","typeKey":"WikiPageOperationFailedException","errorCode":0,"eventId":3000}
Indeed, this is the root error cause you need focus on.
When you run Rest api with powershell task in Azure Devops pipeline, at this point, the request user account of this api is {Projectname} Build service({Orgname}). In other words, the user which is requesting to add the wiki page during the build is {Projectname} Build service({Orgname}), which is a build service account that permission scope is only in pipeline.
Also, since the Wiki is hosted in Repository, to resolve this issue, you must add this Project Build service(Org name) account into your Repository Permission group, and ensure its Contribute
permission as Allow. Thus this build service account can have enough permission to add wiki page:
(Public is my project name, ForMerlin is my org name)
To let you more clear get that why you receive 400 Bad request error
instead of this non-permission error
after you print out $_.Exception.Message;$_.Exception.Response.StatusCode.value__ ; $_.Exception.Response.StatusDescription;
, I reproduced this issue then check our backend IIS log.
When executing this api, in fact, the server is calling Microsoft.TeamFoundation.Wiki.Web.Controllers.WikiPagesController.CreateOrUpdatePage
to pack "request body" which will send to the next operation method used. (Note: this "request body" does not equal with the normal one we used in rest API. Here I means the completed one which is the server needed). In its parameters, there include one which can represent user: callerName
.
As I mentioned previously, the user account which is calling this API request is the build service account, and its permission scope does not meet the server requirement. So, this "request body" that server needed is invalid. Then you received 400 code with the command $_.Exception.Response.StatusCode.value__
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With