How to edit a file using Azure DevOps REST API without passing the file version - azure-devops-rest-api

I'm trying to edit a file's last version on TFS using the example shown over here:
https://learn.microsoft.com/en-us/rest/api/azure/devops/tfvc/changesets/create?view=azure-devops-rest-5.0
But this request demands the file's version to be edited.
Is there a way to edit a file's last version without passing the actual file version?
Thanks in advance,

Is there a way to edit a file's last version without passing the actual file version?
Based on my test, the version field is required.
So When you use the Rest API to edit a file from TFVC repo, you need to input the latest version.
But you could use the Rest API:Changesets - Create to get the latest version number
Here is a PowerShell Script example:
$token = "PAT"
$url="https://dev.azure.com/{Organization Name}/{Project Name}/_apis/tfvc/changesets?$orderby=id des&api-version=5.0"
$token = [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes(":$($token)"))
$response = Invoke-RestMethod -Uri $url -Headers #{Authorization = "Basic $token"} -Method Get -ContentType application/json
$latestversion = $response.value.changesetId[0]
echo $latestversion
$url2="https://dev.azure.com/{Organization name}/_apis/tfvc/changesets?api-version=5.0"
$body = "{
`"changes`": [
{
`"item`": {
`"version`": `"$latestversion`",
`"path`": `"$/TFVCTEST/test.txt`",
`"contentMetadata`": {
`"encoding`": 1200,
`"contentType`": `"text/plain`"
}
},
`"changeType`": `"edit`",
`"newContent`": {
`"content`": `"Initial coassdntentssss sas of 4/9/2019 11:21:13 PM\nEdited contents`"
}
}
],
`"comment`": `"(sample) Editing the file via API`"
}"
$response2 = Invoke-RestMethod -Uri $url2 -Headers #{Authorization = "Basic $token"} -Method Post -Body $body -ContentType application/json
Explanation:
The first URL is used to get all changestIDs, the results are sorted in descending order by default. So you only need to take the first value that is the latest value.
Then you could set the latest value as version in the request body of second Rest API to edit the files.
By the way, considering that you are using TFS, you may need to change the url to the following:
Changesets - Get Changesets
Changesets - Create

Related

Azure KeyVault Encrypt API Endpoint Not Returning Base64 Encoded String

In PowerShell I'm calling the KeyVault Encrypt API endpoint to encrypt a connection string.
First I create a byte array of the connection string and then I create a base64 string. Then I call the /encrypt endpoint
[byte[]]$bytes = [System.Text.Encoding]::Unicode.GetBytes($targetConnectionString)
echo "Plain Bytes" $bytes
#$base64Array = [Convert]::ToBase64String($bytes)
#$EncryptedTargetConnectionString = Encrypt-ByteArray -accessToken $kvToken -vaultName $kvName -keyID $encryptionKeyId -plainArray $bytes
# converting the byte array to Base64 string
# Note from [Chris Clayton (AzureCAT)].
# "When performing the post methods there is a common theme of converting to Base64 strings which just makes sense so we do not introduce any invalid characters etc. and creating a JSON body as you can see here"
$base64Array = [Convert]::ToBase64String($bytes)
echo "Base 64 Array" $base64Array
# constructing the url for the vault encrypt operation by specifying the KeyID and Key version. If key version is not specified, then the most recent key version will be used
$queryUrl = "$encryptionKeyId" + '/encrypt?api-version=2016-10-01'
#echo "Query URL: " $queryUrl
# injecting the access token we obtained in the authorization header
$headers = #{ 'Authorization' = "Bearer $kvToken"; "Content-Type" = "application/json" }
echo "KV Token" $kvToken
# construct the body with our data to be encrypted and specifying RSA as encryption alg.
$bodyObject = #{ "alg" = "RSA-OAEP"; "value" = $base64Array }
$bodyJson = ConvertTo-Json -InputObject $bodyObject
# invoking a POST call to do the magic
$encryptionResponse = Invoke-RestMethod -Method POST -Ur $queryUrl -Headers $headers -Body $bodyJson
That seems to be working, because when I use Postman to call the Decrypt endpoint I get the response I'd expect.
Then I call an Azure Function which will need to be able to decrypt this connection string.
$EncryptedTargetConnectionString = $encryptionResponse.value
$webRequestBody = #"
{
"BlobUrl" : "$blobUrl",
"Environment" : "$env",
"State" : "$state",
"EncryptedConnectionStrings" : [
{
"EncryptedConnectionString": "$EncryptedTargetConnectionString"
}
]
}
"#
$webRequest = Invoke-WebRequest http://localhost:7071/api/DisableReplication -Headers #{"Accept" = "application/json"} -Method 'POST' -ContentType 'application/json; charset=utf-8' -Body $webRequestBody -UseBasicParsing
What's crazy here, though, is that the RESPONSE from the /encrypt endpoint doesn't seem to be in base64 format.
When I try to run Convert.FromBase64String(encryptedString) in C# against the encrypted string, it doesn't work. It tells me about it not being it base64 format.
C# Code
byte[] inputAsByteArray = Convert.FromBase64String(encryptedString);
// Encryption algorithm MUST match what was used in the PowerShell task in the Release Pipeline to encrypt the string
// Ex: $bodyObject = #{ "alg" = "RSA-OAEP"; "value" = $base64Array }
DecryptResult decryptResult = await cryptographyClient.DecryptAsync(JsonWebKeyEncryptionAlgorithm.RSAOAEP, inputAsByteArray);
string decryptedString = Encoding.UTF8.GetString(decryptResult.Plaintext);
return decryptedString;
None of the Key Vault SDK Clients that I've found for .NET Core support decrypting a string directly, and I can't seem to convert the string into a byte array the way I'd expect to be able to.
Any idea what kind of mind games Microsoft is playing here?
Key Vault uses Base64Url Encoding which is slightly different than Base64 encoding.
Base64URL encoding is nearly identical to Base64 encoding except for the fact that it substitutes characters that are not safe in a URL ('+','~', etc.) with other characters. In addition, it does not use padding ('=').

Does the Azure AD B2C Graph endpoint support UTF-8 charset for uploading policies?

I have a custom policy XML document that contains characters that need UTF-8 to be rendered correctly (A country name of Curaçao)
When I manually run my CI scripts which use UTF-8 and upload the policy to Azure B2C's UI, I see the characters as expected.
However, despite reading the file using the UTF-8 encoding, when I call the graph.microsoft.com/beta/trustframework/policies/ endpoint for my PUT request, the resulting policy appears to not be rendered using UTF-8.
I have attempted the following for my content headers
$headers.Add("Content-Type", 'application/xml') -- the original, where I first saw the problem
$headers.Add("Content-Type", 'application/xml; charset=UTF-8') -- no change
$headers.Add("Content-Type", 'application/xml; charset="UTF-8"') -- 500 error from server
$headers.Add("Content-Type", 'application/xml; charset=utf-8') -- the issue still persists.
Any thoughts? I'm sure it must be something I'm missing.
The full powershell excerpt of what I'm doing:
$headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
$headers.Add("Content-Type", 'application/xml; charset=utf-8')
$headers.Add("Authorization", 'Bearer ' + $token)
$graphuri = 'https://graph.microsoft.com/beta/trustframework/policies/' + $PolicyId + '/$value'
$policycontent = Get-Content $PathToFile -Encoding UTF8
$response = Invoke-RestMethod -Uri $graphuri -Method Put -Body $policycontent -Headers $headers
Image of the result:
The following post helped: Deploy custom policies with Azure Pipelines changing the charset encoding
Change
$response = Invoke-RestMethod -Uri $graphuri -Method Put -Body $policycontent -Headers $headers
To
$response = Invoke-RestMethod -Uri $graphuri -Method Put -Body $policycontent -Headers $headers -ContentType "application/xml; charset=utf-8"

Mircosoft Graph API Authentication token by using v2.0 and v1.0

$LoginUrl = "https://login.microsoft.com"
$Tenantname = "xxxx"
$Body = # {
client_id = "xxxx"
client_secret = "xxxx"
resource = "https://graph.microsoft.com"
grant_type = "client_credentials"
}
$oauth = Invoke-RestMethod -Method Post -Uri $LoginUrl/$Tenantname/oauth2/v2.0/token -Body $Body
Above is my Authentication to get token for Graph API, application registration has been done. Below error when I use the v2.0 for token.
Invoke-RestMethod: {"error":"invalid_request","error_description":"AADSTS901002: The 'resource' request parameter is not supported" ......}
But, I can get it run when I used below, that I searched from website.
I got confused where is wrong, and why below v1.0 is working.
$oauth = Invoke-RestMethod -Method Post -Uri $LoginUrl/$Tenantname/oauth2/token?api-version=1.0 -Body $Body
As documented here, you don't need the resource-parameter (you can read that from the error message) for v2-auth. But what you are missing and is required according to the docu:
scope
Required
The value passed for the scope parameter in this request should be the resource identifier (Application ID URI) of the resource you want, affixed with the .default suffix. For Microsoft Graph, the value is https://graph.microsoft.com/.default. This value informs the Microsoft identity platform endpoint that of all the application permissions you have configured for your app, it should issue a token for the ones associated with the resource you want to use.

POST request errors out when CURL works

I’m trying to use a cloudflare worker (Pasted below) to send an SMS message via the Twilio API. The CURL request (also pasted below) I’m basing the worker off of works.
Based on the 400 error from the worker the message body isn’t passed in correctly
{"code": 21602, "message": "Message body is required.", "more_info": "https://www.twilio.com/docs/errors/21602", "status": 400}
but the code looks fine to me. We can at least confirm the header is passed correctly because messing with the authorization value changes the error.
I also looked at the example POST request in the template gallery and can’t see a reason for the failure.
https://developers.cloudflare.com/workers/templates/pages/post_json/
What do i need to change in my worker code to make the POST request work?
Note: i recognize i shouldn’t put the Twilio Auth token in the body but I’ll rotate the key later.
async function handleRequest(request) {
const init = {
body: JSON.stringify(body),
method: 'POST',
headers: {
'content-type': 'application/json',
'Authorization': "Basic " + btoa('[account id]:[Authtoken]'),
},
}
return await fetch(url, init)
}
addEventListener('fetch', event => {
return event.respondWith(handleRequest(event.request))
})
const url = 'https://api.twilio.com/2010-04-01/Accounts/[Account id]/Messages.json'
const body = {
Body:"Hello World",
From:"+[some number]",
To:"+[some number]]",
}
curl 'https://api.twilio.com/2010-04-01/Accounts/[Account id]/Messages.json' -X POST \
--data-urlencode 'To=+[some number]' \
--data-urlencode 'From=+[some number]' \
--data-urlencode 'Body=Hello World' \
-u [account id]:[auth token]
because Twilio requires application/x-www-form-urlencoded.
REST API: Your Request
Creating or updating a resource involves performing an HTTP PUT or
HTTP POST to a resource URI. In the PUT or POST, you represent the
properties of the object you wish to update as form urlencoded
key/value pairs. Don't worry, this is already the way browsers encode
POSTs by default. But be sure to set the HTTP Content-Type header to
"application/x-www-form-urlencoded" for your requests if you are
writing your own client.

Why am I getting a 400 - Bad Request when completing a pull request using the VSTS API

I'm guessing this is a permission issue. I know the URI and body, etc are all correct, as it works fine with a different authorisation in the header.
I tried adjusting permissions under _admin/_versioncontrol to 'Allow', as per what had worked for me when creating pull requests. Ref:
Getting a '403 Forbidden' when calling the Pull Request API on VSTS
But while I can create/abandon requests just fine, I can't seem to auto-complete them.
What am I missing?
Edit 1 (based on starian chen-MSFT's request):
When I catch the exception in my powershell script and do a Write-Output $_.Exception, I get this:
The remote server returned an error: (400) Bad Request.
When I remove the Invoke-RestMethod from a try-catch block, I get this printed to the consolve output:
2018-03-10T03:22:13.3706092Z Calling https://<my_vsts_account>.visualstudio.com/DefaultCollection/_apis/git/repositories/<my_git_repo_id>/pullRequests/<my_pr_id>?api-version=3.0 with body:
2018-03-10T03:22:13.3727606Z {
2018-03-10T03:22:13.3727838Z "autoCompleteSetBy": {
2018-03-10T03:22:13.3728135Z "id": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee"
2018-03-10T03:22:13.3728388Z },
2018-03-10T03:22:13.3728593Z "completionOptions": {
2018-03-10T03:22:13.3728942Z "mergeCommitMessage": "Auto completing pull request",
2018-03-10T03:22:13.3729252Z "squashMerge": "false",
2018-03-10T03:22:13.3729845Z "deleteSourceBranch": "false"
2018-03-10T03:22:13.3730053Z }
2018-03-10T03:22:13.3730229Z }
2018-03-10T03:22:13.6040452Z ##[error]Invoke-RestMethod : {"$id":"1","innerException":null,"message":"Invalid argument value.\r\nParameter name: Invalid
pull request auto complete set by id. Valid values are either the current user identity id, or an empty guid (to unset
auto complete).","typeName":"Microsoft.TeamFoundation.SourceControl.WebServer.InvalidArgumentValueException,
Microsoft.TeamFoundation.SourceControl.WebServer","typeKey":"InvalidArgumentValueException","errorCode":0,"eventId":0}
At C:\Development\.agent\scripts\do-ci.ps1:562 char:33
+ ... ry_result = Invoke-RestMethod -Uri $uri -Method Patch -UseDefaultCred ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (System.Net.HttpWebRequest:HttpWebRequest) [Invoke-RestMethod], WebExc
eption
+ FullyQualifiedErrorId : WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeRestMethodCommand
Once again, when I copy paste the above URI and body into Postman, it works perfectly fine. Seeing that the only difference between my Powershell script and the Postman call is the authorization header, it means this is a permission issue, and a permission issue alone.
You need to specify the same user id (current authorized user) to update pull request to Auto-Complete through REST API ("autoCompleteSetBy":{"id":xxx}), otherwise you will get 400 error.
You can update pull request status to Completed through REST API if you just want to complete the pull request.
Update:
Simple code to create pull request and get user's id:
param(
[string]$project,
[string]$repo,
[string]$sourceBranch,
[string]$targetBranch,
[string]$title,
[string]$des,
[string]$token
)
$bodyObj=#{
"sourceRefName"="refs/heads/$sourceBranch";
"targetRefName"= "refs/heads/$targetBranch";
"title"= "$title";
"description"="$des";
}
$bodyJson=$bodyObj| ConvertTo-Json
$uri="https://XXX.visualstudio.com/DefaultCollection/$project/_apis/git/repositories/$repo/pullRequests?api-version=3.0"
Write-Output $bodyJson
Write-Output $uri
$base64AuthInfo = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(("{0}:{1}" -f "test",$token)))
$result=Invoke-RestMethod -Method POST -Uri $Uri -ContentType "application/json" -Headers #{Authorization=("Basic {0}" -f $base64AuthInfo)} -Body $bodyJson
$serviceId=$result.createdBy.id
Write-Host $serviceId
Write-Host "##vso[task.setvariable variable=userId;]$serviceId"

Resources