Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using OAuth and PowerShell to Update Azure DevOps Wiki Pages

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?

like image 522
PoorInRichfield Avatar asked Oct 11 '19 12:10

PoorInRichfield


1 Answers

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:

enter image description here

(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__.

like image 136
Mengdi Liang Avatar answered Nov 15 '22 12:11

Mengdi Liang