Issue when updating build definition using the REST api of VSTS - azure-devops

I want to update a variable in my build definition but when it tries to run the Invoke-RestMethod I receive the following exception:
Invoke-RestMethod : {"$id":"1","innerException":null,"message":"This request expects an object in the request body,
but the supplied data could not be
deserialized.","typeName":"Microsoft.TeamFoundation.Build.WebApi.RequestContentException,
Microsoft.TeamFoundation.Build2.WebApi, Version=14.0.0.0, Culture=neutral,
PublicKeyToken=b03f5f7f11d50a3a","typeKey":"RequestContentException","errorCode":0,"eventId":3000}
At D:\a\_temp\231f1be5-edc0-4bd9-a2e4-efd23a8308d1.ps1:42 char:1
+ Invoke-RestMethod -Method Put -Uri "$($projectDef.Url)&api-version=2. ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (System.Net.HttpWebRequest:HttpWebRequest) [Invoke-RestMethod], WebExc
eption
+ FullyQualifiedErrorId : WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeRestMethodCommand
Everything else seems to work, I get the build definition, I can update the variable but when I try to PUT the json back using the Invoke-RestMethod it fails.
Below is the used code which runs in a Powershell inline script:
# This script is intended to be used for PowerShell script tasks in VSTS in "inline mode"
$valueName = 'ProjectBuildNumber'
$token = 'MYTOKENCODE'
$uriRoot = $env:SYSTEM_TEAMFOUNDATIONSERVERURI
$ProjectName = $env:SYSTEM_TEAMPROJECT
$ProjectId = $env:SYSTEM_TEAMPROJECTID
$uri = "$uriRoot$ProjectName/_apis/build/definitions?api-version=2.0"
# Base64-encodes the Personal Access Token (PAT) appropriately
$base64AuthInfo = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(("{0}:{1}" -f "", $token)))
$header = #{Authorization = ("Basic {0}" -f $base64AuthInfo)}
# Get the list of Build Definitions
$buildDefs = Invoke-RestMethod -Uri $uri -Method Get -ContentType "application/json" -Headers $header
# Find the build definition for this project
$buildDef = $buildDefs.value | Where-Object { $_.Project.id -eq $ProjectId }
if ($buildDef -eq $null)
{
Write-Error "Unable to find a build definition for Project '$ProjectName'. Check the config values and try again." -ErrorAction Stop
}
# NOTE: ensure we call the v 2.0 api! (both get and put calls need the same api versions!)
# get its details
$projectDef = Invoke-RestMethod -Uri "$($buildDef.Url)?api-version=2.0" -Method Get -ContentType "application/json" -Headers $header
if ($projectDef.variables.$valueName -eq $null)
{
Write-Error "Unable to find a variable called '$valueName' in Project $ProjectName. Please check the config and try again." -ErrorAction Stop
}
# get and increment the variable in $valueName
[int]$counter = [convert]::ToInt32($projectDef.variables.$valueName.Value, 10)
$updatedCounter = $counter + 1
Write-Host "Project Build Number for '$ProjectName' is $counter. Will be updating to $updatedCounter"
# Update the value and update VSTS
$projectDef.variables.$valueName.Value = $updatedCounter.ToString()
$projectDefJson = $projectDef | ConvertTo-Json -Depth 50 -Compress
# when we build the URL need to cater for if the Project Definition URL already has parameters or not.
$separator = "?"
if ($projectDef.Url -like '*?*')
{
$separator = "&"
}
$putUrl = "$($projectDef.Url)$($separator)api-version=2.0"
Write-Host "Updating Project Build number with URL: $putUrl"
Invoke-RestMethod -Method Put -Uri $putUrl -Headers $header -ContentType "application/json" -Body $projectDefJson | Out-Null
UPDATE
When I use postman to test this, I first run a get and then a put, it works...

Ok problem was with the character & which got replaced by \u0026. Added the following which solved this:
([System.Text.Encoding]::UTF8.GetBytes($projectDefJson))
So the last line becomes:
Invoke-RestMethod -Method Put -Uri $putUrl -Headers $header -ContentType "application/json" -Body ([System.Text.Encoding]::UTF8.GetBytes($projectDefJson)) | Out-Null

Related

How to assign users office365 licenses using groups with Microsoft graph and PowerShell

I'm working on a PowerShell script to assign users office 365 license based on group (security group). So, i have created app registration and assigned the required API permissions.
When I try to run my script, i get the error below
Invoke-RestMethod : The remote server returned an error: (400) Bad Request.
At line:1 char:1
+ Invoke-RestMethod -Uri $uri -Body $body -ContentType "application/jso ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (System.Net.HttpWebRequest:HttpWebRequest) [Invoke-RestMethod], WebException
+ FullyQualifiedErrorId : WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeRestMethodCommand
Below is the entire script
$connectiondetails = #{
# This ids and secret are present in the overview and certificate & secret page of our application in azure AD
# Tenant ID here
'tenantid' = ""
# Application (client) ID here
'clientid' = ""
# Secret id here
'ClientSecret' = "" | ConvertTo-SecureString -AsPlainText -Force
}
$token = Get-MsalToken #connectiondetails
$tokenid_ = $token.AccessToken
# $uri = "https://graph.microsoft.com/v1.0/groups"
# $grp = Invoke-RestMethod -Uri $uri -Headers #{Authorization=("bearer {0}" -f $tokenid_)}
# $grp
$uri = "https://graph.microsoft.com/v1.0/groups/ffbabc6f-aa87-40f3-8665-9d140e4a7adb/assignLicense"
$body = "{""SkuId"":""cbdc14ab-d96c-4c30-b9f4-6ada7cdc1d46""}"
# assign license call
Invoke-RestMethod -Uri $uri -Body $body -ContentType "application/json" -Method post -Headers #{Authorization=("bearer {0}" -f $tokenid_)}
Permissions assigned to the app
I need assistance to know what am doing wrong. Thank you.
Solutions tried
The body for the request to add license requires addLicenses property with permissions Group.ReadWrite.All and Directory.ReadWrite.All.
{
"addLicenses": [
{
"skuId": "cbdc14ab-d96c-4c30-b9f4-6ada7cdc1d46"
}
],
"removeLicenses": []
}
PS
$uri = "https://graph.microsoft.com/v1.0/groups/ffbabc6f-aa87-40f3-8665-9d140e4a7adb/assignLicense"
# create json object
$data = #{
"addLicenses" = #(
#{
"skuId" = "cbdc14ab-d96c-4c30-b9f4-6ada7cdc1d46"
}
)
"removeLicenses" = #()
}
# convert to JSON-formatted string
$body = $data | ConvertTo-Json
# assign license call
Invoke-RestMethod -Uri $uri -Body $body -ContentType "application/json" -Method post -Headers #{Authorization=("bearer {0}" -f $tokenid_)}
Resources:
Group - assign licenses
This article seems to explain the script building in detail
Create MS Graph scripts with AAD App

Invoke-RestMethod from PowerShellScript doesn't work but work from commandline

I am trying to invoke a rest method from powershell script to get git changes from azure repo. When i run the whole process one by one command it works but when i try to run the whole script by writing a powershell script it doesn't work as expected giving empty output. can anyone tell what could be the problem here
$url = "https://dev.azure.com/<Org_Name>/<Project_Name>/_apis/git/repositories/<RepoID>/commits/$(Build.SourceVersion)/changes?api-version=5.1"
$userName = "Username"
$password = "<PAT>"
$base64AuthInfo = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(("{0}:{1}" -f $userName,$password)))
$result = Invoke-RestMethod -Uri $url -Headers #{Authorization = "Basic $base64AuthInfo"} -Method GET
Write-Host $result
$changesFolders = $result.changes | Where-Object {$_.item.gitObjectType -eq "tree"} | Select-Object -Property{$_.item.Path}
foreach($path in $changesFolders)
{
Write-Host $path
}
Write-Host $changesFolder
I am not sure how but i tried Invoke-webRequest instead of Invoke-RestMethod and it worked
But I had to make some changes in the header as well otherwise it was hitting the endpoint but still giving 203 status
$headers = #{
Authorization=("Basic" + [System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes(":$PAT")));
ContentType="application/json"
}
$result = Invoke-webRequest -Uri $url -Headers $header -Method GET
Write-Host $result.StatusCode
Gives result and status code 200 as expected
note: without adding a colon before password/PAT while converting to base64string will throw 203 status
The following example is working. Additionally, you can paste our resulting URL into the browser and check the answer from your org.
$user = ""
$token = "PAT"
$base64AuthInfo = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(("{0}:{1}" -f $user,$token)))
$orgUrl = "ORG"
$teamProject = "PROJECT"
$repoName = "REPONAME"
$commitId = "COMMITID"
$restApiGetCommit = "$orgUrl/$teamProject/_apis/git/repositories/$repoName/commits/$commitId/changes?api-version=5.1"
function InvokeGetRequest ($GetUrl)
{
return Invoke-RestMethod -Uri $GetUrl -Method Get -ContentType "application/json" -Headers #{Authorization=("Basic {0}" -f $base64AuthInfo)}
}
Write-Host "Url" $restApiGetCommit
$commitInfo = InvokeGetRequest $restApiGetCommit
Write-Host "Commit: " $commitInfo.changes
foreach($path in $commitInfo.changes)
{
Write-Host "Path" $path.item.path
}

Azure DevOps Server Invoke-RestMethod Error: No API version provided for the "PUT" request

Based on this PowerShell example from Etienne Tremblay I try to pass data to the Azure DevOps Server (2019.0.1) REST API.
In this example I would like to modify the description text of a project (just for test reasons):
# DEMO 5 Update an environement build variable
Write-Host "Demo 5"
$projects.value | ForEach-Object {
$project = $_.name
$buildManagementAreaId = "efc2f575-36ef-48e9-b672-0c6fb4a48ac5"
$tfsBaseUrl = GetUrl -orgUrl $orgUrl -header $header -AreaId $buildManagementAreaId
# https://learn.microsoft.com/en-us/rest/api/azure/devops/build/definitions/list?view=azure-devops-rest-5.0
$relDefUrl = "$tfsBaseUrl$project/_apis/build/definitions?api-version=5.0"
Write-Host "Requesting URL: $relDefUrl" -ForegroundColor Yellow
$result = Invoke-RestMethod $relDefUrl -Method Get -ContentType "application/json" -Headers $header
$relDefs = $result.value
if($relDefs.count -gt 0){
Write-Host "$project $($relDefs.count) build def founds" -ForegroundColor Blue
$relDefs | ForEach-Object {
$relDef = $_
$relDefExpanded = Invoke-RestMethod "$($relDef.url)?`$Expand=Environments&api-version=5.0" -Method Get -ContentType "application/json" -Headers $header
$relDefExpanded.project.description = "Hallo!!!"
$body = $relDefExpanded | ConvertTo-Json -Depth 100 -Compress
$body = [System.Text.Encoding]::UTF8.GetBytes($body)
#ERROR OCCURS HERE
$updateResult = Invoke-RestMethod "$($relDef.url)?api-version=5.0" -Method Put -ContentType "application/json" -body $body -Headers $header
Write-host "Variable value after: $($updateResult.project.description)" -ForegroundColor Green
}
}
}
Hints:
$projects get defined before in the script (demo1)
the original script change a variable not a description (both versions don't work)
The Error occurs on:
$updateResult = Invoke-RestMethod "$($relDef.url)?api-version=5.0" -Method Put -ContentType "application/json" -body $body -Headers $header
with the message:
Invoke-RestMethod: {"$ id": "1", "innerException": null, "message": "No API version provided for the" PUT "request The version must either be part of the" Accept "header (eg
\ "application / json; api-version = 1.0 \") or as a query parameter (for example, "? api-version = 1.0")
. Will "," type name ":" Microsoft.VisualStudio.Services.WebApi.VssVersionNotSpecifiedException,
Microsoft.VisualStudio.Services.WebApi","typeKey":"VssVersionNotSpecifiedException","errorCode":0,"eventId":3000}
In C:\Users\mkober\Desktop\Azure DevOps Console\WriteAPI.ps1:75 Zeichen:29
+ ... ateResult = Invoke-RestMethod "$($relDef.url)?api-version=5.0" -Metho ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (System.Net.HttpWebRequest:HttpWebRequest) [Invoke-RestMethod], WebException
+ FullyQualifiedErrorId : WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeRestMethodCommand
Final URL:
http://138.202.18.216:8070/Samples/19e02b27-74ab-40dd-a519-ece38fafa870/_apis/build/Definitions/11?revision=5?api-version=5.0
You just need to fix the url, change the ? before the api-version to &:
?revision=5&api-version=5.0
The revision and the api-version is parameters, if you have only one parameter you use ? but when you want to use more you need to append them with &.

How can I cancel & delete a waiting build in the queue using PowerShell

Due to long running builds various next in line builds take longer time to execute.
Is there a way I can cancel and delete waiting build in queue and give way for latest triggered build using PowerShell or REST API's?
The following code snippet goes over all TFS builds,Gets the Builds which in Progress and not started, and then, cancel them.
$tfsUrl = "http://{server}:{port}/{organization}/{collection}/{project}" # TFS Base URL
$BuildDefsUrl = "$tfsUrl/_apis/build/definitions?api-version=2.0" # TFS build definitions URL
$BuildsUrl = "$tfsUrl/_apis/build/builds" #TFS Builds URL
$Builds = (Invoke-RestMethod -Uri ($BuildDefsUrl) -Method GET -UseDefaultCredentials).value | Select id,name # get all builds
#for filtering use : | Where-Object {$_.name -like "*Your Pattern*"}
foreach($Build in $Builds)
{
$command = "$($BuildsUrl)?api-version=3.2-preview.3&resultFilter=inprogress&definitions=$($Build.id)&queryOrder=finishTimeDescending"
$Ids = (((Invoke-RestMethod -Method Get -Uri $command -UseDefaultCredentials).value) | where status -like "*notStarted*").id # get waiting builds id's
foreach($id in $Ids)
{
$uri = "$($BuildsUrl)/$($id)?api-version=2.0" # TFS URI
$body = '{"status":4}' # body
$result = Invoke-RestMethod -Method Patch -Uri $uri -UseDefaultCredentials -ContentType 'application/json' -Body $body -Verbose #cancel build
}
}
The above example is pretty old. The code snippet below working for Azure Devops-
$PATToken = "PAT_GOES_HERE"
$AuthHeader= #{Authorization = 'Basic ' + [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(":$($PATToken)")) }
$azureDevops = "https://dev.azure.com/{organization}/{project}"
$BuildsUrl = "$azureDevops/_apis/build/builds"
$filterBuilds = "$($BuildsUrl)?statusFilter=notStarted&api-version=6.0"
(Invoke-RestMethod -Method Get -Uri $filterBuilds -Headers $AuthHeader).value | % {
$uri = "$($BuildsUrl)/$($_.id)?api-version=6.0" # Azure Devops URI
$body = '{"status":4}' # body
$result = Invoke-RestMethod -Method Patch -Uri $uri -Headers $AuthHeader -ContentType 'application/json' -Body $body -Verbose #cancel build
Write-Output "$($_.definition.name) cancaled"
}

How to attach CSV file to Service Now incident via REST API using PowerShell?

I need to attach the file either xlsx or CSV to a particular incident via SNOW REST API using PowerShell script. I have tried with the below code:
if (!$script:ServiceNowCreds) {
$script:ServiceNowCreds = Get-Credential
}
$snow_url = 'https://dev652xx.service-now.com/api/now/table/incident'
$Body = #{
'number' = 'INC00xx059'
}
$result = Invoke-RestMethod -Uri $snow_url -Credential $script:ServiceNowCreds -Body $Body -ContentType "application/json"
$result.result | select sys_id, number | ForEach-Object {
$Upload_snow_url ='https://dev652xx.servicenow.com/api/now/attachment/upload'
$headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
$headers.Add('Content-Type','text/csv')
$headers.Add('Accept','*/*')
$sys_id = $_.sys_id
$incident_number = $_.number
$UploadBody = #{
'table_name'='incident';
'table_sys_id'=$sys_id;
'file_name' = 'C:\Users\suganthanraj.p\Documents\Servers.csv'
}
$uploadParam = $UploadBody | ConvertTo-JSon
Write-Host $sys_id
Write-Host $incident_number
$UploadResult = Invoke-RestMethod -Uri $Upload_snow_url -Credential $script:ServiceNowCreds -Body $uploadParam -Method Post -Headers $headers
$UploadResult
}
When I execute the above script I am getting the below error:
Invoke-RestMethod : The remote server returned an error: (415) Unsupported
Media Type.
At C:\Users\suganthanraj.p\Desktop\SNOW-UploadAttachment.ps1:39 char:21
+ ... oadResult = Invoke-RestMethod -Uri $Upload_snow_url -Credential $scr ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (System.Net.HttpWebRequest:HttpWebRequest) [Invoke-RestMethod], WebException
+ FullyQualifiedErrorId : WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeRestMethodCommand
Try changing you content type to "multipart/form-data"
$headers.Add('Content-Type','multipart/form-data')
$UploadBody = #{
'table_name'='incident';
'record_sys_id'=$sys_id;
'uploadFile' = 'C:\Users\suganthanraj.p\Documents\Servers.csv'
}
The error says "The remote server returned an error: (415) Unsupported
Media Type."
Doco on the api can be found here:
https://docs.servicenow.com/bundle/geneva-servicenow-platform/page/integrate/inbound_rest/reference/r_AttachmentAPI-POSTmultipart.html
Your best option would be leverage the OOB Attachment API in ServiceNow. You will need to make a post call from powershell. Powershell has two options for this Invoke-RestMethod and Invoke-WebRequest. I have had better luck with the latter when trying to POST. You might also first build your rest call in Postman make sure you can get the attachment into ServiceNow, then worry about writing your PS.
$Body = #{
User = 'jdoe'
password = 'P#S$w0rd!'
}
$LoginResponse = Invoke-WebRequest 'http://www.contoso.com/login/' - SessionVariable 'Session' -Body $Body -Method 'POST'
$Session
$ProfileResponse = Invoke-WebRequest 'http://www.contoso.com/profile/' -`WebSession $Session $ProfileResponse`
Finally i found answer from the below link
https://community.servicenow.com/community?id=community_question&sys_id=d3707023dbaceb8023f4a345ca961949 and below is the code:
# Eg. User name="admin", Password="admin" for this code sample.
$user = "admin"
$pass = "XXX"
# Build auth header
$base64AuthInfo = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(("{0}:{1}" -f $user, $pass)))
# Set proper headers
$headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
$headers.Add('Authorization',('Basic {0}' -f $base64AuthInfo))
$headers.Add('Accept','application/json')
$headers.Add('Content-Type','application/json')
# Specify endpoint uri
$uri = "https://dev652XX.service-now.com/api/now/attachment/file?table_name=incident&table_sys_id=850XXXXX2200e0ef563dbb9a71c1&file_name=TreeSizeReport.csv"
# Specifiy file to attach
$fileToAttach = "C:\Users\suganthanraj.p\Desktop\TreeSizeReport.csv"
# Specify HTTP method (POST, PATCH, PUT)
$method = "POST"
# Send HTTP request
$response = Invoke-WebRequest -Headers $headers -Method $method -Uri $uri -InFile $fileToAttach
# Print response
$response.RawContent