I want to edit a Azure DevOps wiki page over the REST API (Azure DevOps Server 2019.0.1).
When I run this PowerShell script:
#VARIABLES
$api = "api-version=5.0"
$root = "http://136.202.18.216:8070/Samples"
$personalToken = "uwawlzqp6j7i1nd5dasspwkwp63tr2w2sxb5563zrla2bivynbza"
#AUTHORIZATION
$token = [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes(":$($personalToken)"))
$headers = #{
"Authorization" = ('Basic {0}' -f $token)
"If-Match" = '*'
}
#PROJECT VARIABLES
$project = "Framework%20A"
$pageToUpdate = "/Seite2"
#BODY
$body = #"
{
"content": "Hello"
}
"#
#GET
$url = "$root/$project/_apis/wiki/wikis/2e887fde-ce76-4180-aa8d-f26ed4799eb3/pages?version=wikiMaster&path=$pageToUpdate&includeContent=True&$api"
$content = Invoke-RestMethod -Uri $url -Method Get -ContentType "application/json" -Headers $headers
Write-Host "$($content.path) = $($content.content)" -ForegroundColor Yellow
#PUT
$url = "$root/$project/_apis/wiki/wikis/2e887fde-ce76-4180-aa8d-f26ed4799eb3/pages?version=wikiMaster&path=$pageToUpdate&$api"
$update = Invoke-RestMethod -Uri $url -Method Put -ContentType "application/json" -Headers $header -Body $body -Verbose
Write-Host $update.content -ForegroundColor Yellow
exit(0)
The consolen output is:
/Seite2 = Seite 2 Content
AUSFÜHRLICH: PUT http://136.202.18.216:8070/Samples/Framework A/_apis/wiki/wikis/2e887fde-ce76-4180-aa8d-f26ed4799eb3/pages?version=wikiMaster&path=/Seite2&api-version=5.0 with -1-byte payload
Invoke-RestMethod: {"$ id": "1", "innerException": null, "message": "The required \" IfMatch \ "header specified in the request is an invalid page version Version of the
Wiki page as \ "IfMatch \" header for the request. \ R \ nParameterName: IfMatch "," typeName ":" Microsoft.TeamFoundation.SourceControl.WebServer.InvalidArgumentValueException,
Microsoft.TeamFoundation.SourceControl.WebServer "," TypeKey ":" InvalidArgumentValueException "," error code ": 0," eventId ": 0}
In C:\Users\mkober\Desktop\Azure DevOps Skripte (Bearbeitung)\WikiAPI.ps1:34 Zeichen:11
+ $update = Invoke-RestMethod -Uri $url -Method Put -ContentType "appli ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (System.Net.HttpWebRequest:HttpWebRequest) [Invoke-RestMethod], WebException
+ FullyQualifiedErrorId : WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeRestMethodCommand
The first line /Seite2 = Seite 2 Content is the result of a successful get request from the page I want to edit. The put request occures the error. What am I doing wrong here?
UPDATE: (Working Example)
#VARIABLES
$api = "api-version=5.0"
$root = "http://136.202.18.216:8070/Samples"
$personalToken = "uwawlzqp6j7i1nd5dasspwkwp63tr2w2sxb5563zrla2bivynbza"
#AUTHORIZATION
$token = [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes(":$($personalToken)"))
$headers = #{
"Authorization" = ('Basic {0}' -f $token)
"If-Match" = '{0}'
}
#PROJECT VARIABLES
$project = "Framework%20A"
$pageToUpdate = "/Seite2"
#BODY
$body = #"
{
"content": "Hello"
}
"#
#---------------------------------------------------------------------------------------------------------------------------
#GET
$url = "$root/$project/_apis/wiki/wikis/2e887fde-ce76-4180-aa8d-f26ed4799eb3/pages?version=wikiMaster&path=$pageToUpdate&includeContent=True&$api"
$content = Invoke-WebRequest -Uri $url -Method Get -ContentType "application/json" -Headers $headers
$etag = $content.Headers.ETag
$headers.'If-Match' = $headers.'If-Match' -f $etag
Write-Host "$($content.path) = $($content.content)" -ForegroundColor Yellow
#PUT
$url = "$root/$project/_apis/wiki/wikis/2e887fde-ce76-4180-aa8d-f26ed4799eb3/pages?version=wikiMaster&path=$pageToUpdate&$api"
$update = Invoke-RestMethod -Uri $url -Method Put -ContentType "application/json" -Headers $headers -Body $body -Verbose
Write-Host $update.content -ForegroundColor Yellow
exit(0)
#---------------------------------------------------------------------------------------------------------------------------
In the If-Match header you can't just put '*', you need to put there the ETag of the page.
How do you get it? in your first GET call use Invoke-WebRequest (instaed of Invoke-RestMethod), now in the response you will get also headers response and there the ETag exist:
$page = Invoke-WebRequest -Uri $url ...........
$etag = $page.Headers.ETag
$headers = #{
"Authorizaion = ....."
"If-Match" = $etag
}
Now the second Invoke-RestMethod will work to update the page.
Related
In PowerShell using Invoke-RestMetjhod to call different API´s I am stuck getting an InvalidOperation error when trying to pass both header and body information to the POST call.
My script is:
Set-StrictMode -Version Latest
$ApiToken = Get-Content ‘C:\APIEnergiNet\api_token.txt’
$headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
$headers.Add("Authorization", "Bearer $ApiToken")
$response = Invoke-RestMethod 'https://api.eloverblik.dk/customerapi/api/token' -Method 'GET' -Headers $headers
$GetMetringPointID = Invoke-RestMethod 'https://api.eloverblik.dk/customerapi/api/meteringpoints/meteringpoints?includeAll=true' -Headers #{ Authorization = "Bearer " + $response.result }
foreach($result in $GetMetringPointID){
$CurrentMeteringID = $GetMetringPointID.result.meteringPointId
foreach($Currentresult in $CurrentMeteringID){
$MeterID = $Currentresult
$GetCharges = Invoke-RestMethod 'https://api.eloverblik.dk/customerapi/api/MeteringPoints/MeteringPoint/GetCharges' -Method 'POST' -Headers #{ Authorization = "Bearer " + $response.result } -Body #{ meteringPoints = #( #{meteringPoint = "$Currentresult" } ) }
$GetCharges
}
}
The API needs the following sent in the body :
{
"meteringPoints": {
"meteringPoint": [
"string"
]
}
}
if I create variable $postParams containing the data like this:
$postParams = #{
meteringPoints = #(
#{meteringPoint = "$MeterID" }
)
}
$postParams
it returns:
meteringPoints {System.Collections.Hashtable}
The API has a swagger here https://api.eloverblik.dk/customerapi/index.html
Can anyone help me why I get this error and how to fix it?
Best Regards
Stig :-)
UPDATE WITH LATEST CODE BELOW:
Set-StrictMode -Version Latest
$ApiToken = Get-Content ‘C:\APIEnergiNet\api_token.txt’
$headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
$headers.Add("Authorization", "Bearer $ApiToken")
$response = Invoke-RestMethod 'https://api.eloverblik.dk/customerapi/api/token' -Method 'GET' -Headers $headers
$GetMetringPointID = Invoke-RestMethod 'https://api.eloverblik.dk/customerapi/api/meteringpoints/meteringpoints?includeAll=true' -Headers #{ Authorization = "Bearer " + $response.result }
foreach ($result in $GetMetringPointID)
{
$CurrentMeteringID = $GetMetringPointID.result.meteringPointId
foreach ($Currentresult in $CurrentMeteringID)
{
$MeterID = $Currentresult
$postParams = #{
meteringPoints = #(
#{ meteringPoint = "$MeterID" }
)
} | ConvertTo-Json
$resultlist = Invoke-RestMethod 'https://api.eloverblik.dk/customerapi/api/MeteringPoints/MeteringPoint/GetCharges' -Method 'POST' -ContentType 'application/json' -Headers #{ Authorization = "Bearer " + $response.result } -Body #{ $postParams }
$resultlist
}
}
ERROR I NOW GET:
ParserError: C:\APIEnergiNet\api.ps1:31:245
Line |
31 | … Authorization = "Bearer " + $response.result } -Body #{ $postParams }
| ~
| Missing '=' operator after key in hash literal.
I've made this little PS for you based on yours
$Path = "C:\EnergiNet\"
$TokenFile = $Path + "token.txt"
$JSON_ResponseFile = $Path + "Response.json"
$XML_ResponseFile = $Path + "Response.xml"
$ApiToken = Get-Content $TokenFile
# Get Auth token #
$response = Invoke-RestMethod 'https://api.eloverblik.dk/customerapi/api/token' -Method 'GET' -Headers #{ Authorization = "Bearer " + $ApiToken }
$Auth_Token = $response.result
$body = '{ "meteringPoints": {"meteringPoint": ["571313174xxxxxxxxxxx","571313174xxxxxxxxxxx","571313174xxxxxxxxxxx"] }}'
$headers = #{
'Authorization' = "Bearer " + $response.result
'Accept' = 'application/json'
}
Invoke-RestMethod 'https://api.eloverblik.dk/customerapi/api/meterdata/gettimeseries/2023-01-01/2023-02-01/Quarter' -Method 'POST' -ContentType 'application/json' -Headers $headers -Body $body | ConvertTo-Json -Depth 100 | out-file $JSON_ResponseFile
$headers = #{
'Authorization' = "Bearer " + $response.result
'Accept' = 'application/xml'
}
(Invoke-RestMethod 'https://api.eloverblik.dk/customerapi/api/meterdata/gettimeseries/2023-01-01/2023-02-01/Quarter' -Method 'POST' -ContentType 'application/json' -Headers $headers -Body $body).outerXml | out-file $XML_ResponseFile
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
}
I am trying to use the Microsoft Graph API with Application registration and permissions to modify Planner Tasks.
I have successfully registered my application and set permissions to (Directory.Read.All, Directory.ReadWrite.All, Group.Read.All, Group.ReadWrite.All, GroupMember.Read.All, GroupMember.ReadWrite.All, User.Read). but I can't pull the Plans for any group I always get the error:
> Invoke-RestMethod : { "error": {
> "code": "UnknownError",
> "message": "UserDeleted",
> "innerError": {
> "request-id": "043b140e-aa18-42aa-8672-7c164277553f",
> "date": "2020-05-16T22:47:10"
> } } } At C:\Users\jlamb\Scripts\Connect-MicrosoftGraph.ps1:36 char:17
> + ... $Response = Invoke-RestMethod -Method Get -Headers $headers -Uri $uri ...
> + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> + CategoryInfo : InvalidOperation: (System.Net.HttpWebRequest:HttpWebRequest) [Invoke-RestMethod],
> WebException
> + FullyQualifiedErrorId : WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeRestMethodCommand
I don't understand what this error means.
Here is my sample code:
$tenant = '67a80b53-1b4f-4278-b269-xxxxxxxxxxxx' #Directory ID
$client_id = 'da398f63-3b2b-4dc3-b594-54cbc0a2924f' #Application ID
$scope = 'https://graph.microsoft.com/.default'
$client_secret = 'xxxxx-xxxx.xxxxxxx.xxxxxxxxxxx.xxx' #PowerShellPOC, expires 5/16/2021
$grant_type = 'client_credentials'
if (-not $headers) {
$body = #{client_id=$client_id; scope=$scope; client_secret=$client_secret; grant_type=$grant_type}
$uri = "https://login.microsoftonline.com/$tenant/oauth2/v2.0/token"
$Response = Invoke-RestMethod -Method Post -Uri $uri -Body $body
$access_token = $Response.access_token
$headers = #{
Authorization = "$($Response.token_type) $($Response.access_token)"
ExpiresOn = $($Response.expires_in)
}
}
Write-Host "Getting Groups"
$uri = "https://graph.microsoft.com/v1.0/groups?$orderby=displayName"
$Response = $null
$Response = Invoke-RestMethod -Method Get -Headers $headers -Uri $uri
$Response.value |ft id, displayName
#this works and returns 100 groups
foreach ($groupId in $($Response.value.id)) {
$groupId
$uri = "https://graph.microsoft.com/v1.0/groups/$groupId/planner/plans"
$Response = $null
$Response = Invoke-RestMethod -Method Get -Headers $headers -Uri $uri
$Response.value |ft
}
I've also tried pulling tasks from a plan I know exists:
$plan_id = 'zBgxnzXTNEaGeW9Hz1CVSmQAHpg2'
$uri = "https://graph.microsoft.com/v1.0/planner/plans/$plan_id/tasks"
$uri
$Response = $null
$Response = Invoke-RestMethod -Method Get -Headers $headers -Uri $uri
$Response.value |ft
I get the same error.
I see you are using an app secret, and therefore you are using application permissions that are not supported. Need to be delegated.
Take a look at the Docs pages for the requests, and there you see if app or delegated is supported.
Here you have two ways of getting a token for delegated access:
https://gist.githubusercontent.com/leeford/04fc4c2d4404c2a31a172923d9bed8ee/raw/294f2303b306b4bf7a31f1541ff4d59dc5b40ca2/AzureADGraphAPIUserToken.ps1
https://www.lee-ford.co.uk/graph-api-device-code/
I am trying to clone all the build definitions in VSTS using powershell REST API method. However, i am facing below error. Sharing the code and error which might be useful.
CODE:
Clear-Host
$buildToCloneName = $buildWeWant
$newBuildName = $buildWeWant-Clone
$user = "xxxxxxx"
$accessToken="xxxxxxxx"
$base64AuthInfo = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(("{0}:{1}" -f $user,$accessToken)))
$env:SYSTEM_TEAMFOUNDATIONCOLLECTIONURI = "https://xxxxx.visualstudio.com/"
$env:SYSTEM_TEAMPROJECTID = "xxxxxxx"
"Getting all bulid definitions"
$allSuitesBuildUrl = "$($env:SYSTEM_TEAMFOUNDATIONCOLLECTIONURI)$($env:SYSTEM_TEAMPROJECTID)/_apis/build/definitions?api-version=2.0"
$allSuitedBuilds = Invoke-RestMethod -Uri $allSuitesBuildUrl -Headers #{Authorization=("Basic {0}" -f $base64AuthInfo)}
foreach($buildDetails in $allSuitedBuilds.value){
$buildWeWant = $buildDetails
$buildId = $buildWeWant."id"
[int]$buildIdTest = $null
if(![int]::TryParse($buildId, [ref]$buildIdTest))
{
throw [Exception] "ERROR: NO BUILD ID FOUND"
}
"Getting the exact definition for the build"
# You can see this in the browser using xxxxxxxxxx
$thisBuildDefUrl = "$($env:SYSTEM_TEAMFOUNDATIONCOLLECTIONURI)$($env:SYSTEM_TEAMPROJECTID)/_apis/build/definitions/" + $buildId + "?api-version=2.0"
$thisBuildDefUrl
$thisBuildDef = Invoke-RestMethod -Uri $thisBuildDefUrl -Headers #{Authorization=("Basic {0}" -f $base64AuthInfo)}
## Create a name for the clone by prefixing "_Clone" to the build name
"Assigning a new name"
$thisBuildDef.Name = $buildWeWant."id"."_Clone"
"Creating a clone build with name $newBuildName"
$defAsJson = $thisBuildDef | ConvertTo-Json -Depth 100
$newBuildDefUrl = "$($env:SYSTEM_TEAMFOUNDATIONCOLLECTIONURI)$($env:SYSTEM_TEAMPROJECTID)/_apis/build/definitions?api-version=2.0"
$newBuildDef = Invoke-RestMethod -Uri $thisBuildDefUrl -Headers #{Authorization=("Basic {0}" -f $base64AuthInfo)} -Method Post -Body $defAsJson -ContentType "application/json" -ErrorAction Stop
$newBuildDefAsJson = $newBuildDef | ConvertTo-Json -Depth 100
$newBuildDefAsJson
"New Build Created"
$newBuildDef.Name
}
ERROR:
Invoke-RestMethod : {"$id":"1","innerException":null,"message":"Value
cannot be null.\r\nParameter name:
definition.Name","typeName":"System.ArgumentNullException, mscorlib,
Version=14.0.0.0, Culture=neutral,
PublicKeyToken=b77a5c561934e089","typeKey":"ArgumentNullException","errorCode":0,"eventId":0}
At line:42 char:24
+ ... wBuildDef = Invoke-RestMethod -Uri $thisBuildDefUrl -Headers #{Author ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (System.Net.HttpWebRequest:HttpWebRequest) [Invoke-RestMethod],
WebExceptio n
+ FullyQualifiedErrorId : WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeRestMethodCommand
You can prepare the header separately other than that of inline definition
Clear-Host
$buildToCloneName = $buildWeWant
$newBuildName = $buildWeWant-Clone
$user = "xxxxxxx"
$accessToken = "xxxxxxxx"
$base64AuthInfo = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(("{0}:{1}" -f $user, $accessToken)))
$env:SYSTEM_TEAMFOUNDATIONCOLLECTIONURI = "https://xxxxx.visualstudio.com/"
$env:SYSTEM_TEAMPROJECTID = "xxxxxxx"
"Getting all bulid definitions"
$allSuitesBuildUrl = "$($env:SYSTEM_TEAMFOUNDATIONCOLLECTIONURI)$($env:SYSTEM_TEAMPROJECTID)/_apis/build/definitions?api-version=2.0"
$allSuitedBuilds = Invoke-RestMethod -Uri $allSuitesBuildUrl -Headers #{Authorization = ("Basic {0}" -f $base64AuthInfo) }
foreach ($buildDetails in $allSuitedBuilds.value) {
$buildWeWant = $buildDetails
$buildId = $buildWeWant."id"
[int]$buildIdTest = $null
if (![int]::TryParse($buildId, [ref]$buildIdTest)) {
throw [Exception] "ERROR: NO BUILD ID FOUND"
}
"Getting the exact definition for the build"
# You can see this in the browser using xxxxxxxxxx
$headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
$headers.Add("Authorization", "Basic $base64AuthInfo")
$headers.Add("Content-Type", "application/json")
$thisBuildDefUrl = "$($env:SYSTEM_TEAMFOUNDATIONCOLLECTIONURI)$($env:SYSTEM_TEAMPROJECTID)/_apis/build/definitions/" + $buildId + "?api-version=2.0"
$thisBuildDefUrl
$thisBuildDef = Invoke-RestMethod -Uri $thisBuildDefUrl -Headers $headers
## Create a name for the clone by prefixing "_Clone" to the build name
"Assigning a new name"
$thisBuildDef.Name = $buildWeWant."id"."_Clone"
"Creating a clone build with name $newBuildName"
$defAsJson = $thisBuildDef | ConvertTo-Json -Depth 100
$newBuildDefUrl = "$($env:SYSTEM_TEAMFOUNDATIONCOLLECTIONURI)$($env:SYSTEM_TEAMPROJECTID)/_apis/build/definitions?api-version=2.0"
$newBuildDef = Invoke-RestMethod -Uri $thisBuildDefUrl -Headers $headers -Method Post -Body $defAsJson -ContentType "application/json" -ErrorAction Stop
$newBuildDefAsJson = $newBuildDef | ConvertTo-Json -Depth 100
$newBuildDefAsJson
"New Build Created"
$newBuildDef.Name
}
I need to trigger a build after successful deployment of a release. I have tried using below code in Powershell in the release definition.
After executing, I get this error - Access is denied due to invalid credentials
$url = "http://abc:8080/tfs/GlobalCollection/Project/_apis/build/builds?
api-version=2.0"
$body = "{ 'definition' : { 'id' : 1} }"
$type = "application/json"
$headers = #{
Authorization = "Basic d3JlblxzcsampleTIzNA=="
}
Write-Host "URL: $url"
$definition = Invoke-RestMethod -Uri $url -Body $body -ContentType $type -
Method Post -Headers $headers
Write-Host "Definition = $($definition | ConvertTo-Json -Depth 1000)"`
Based on my test, you can use -UseDefaultCredentials :
$type = "application/json"
$url = "http://abc:8080/tfs/GlobalCollection/Project/_apis/build/builds?api-version=2.0"
$body = "{ 'definition' : { 'id' : 56} }"
Write-Host "URL: $url"
$definition = Invoke-RestMethod -Uri $url -Body $body -ContentType $type -Method Post -UseDefaultCredentials
Write-Host "Definition = $($definition | ConvertTo-Json -Depth 1000)"
Alternatively provide the specific Credential:
$user = "username"
$password = "password"
# Base64-encodes the Personal Access Token (PAT) appropriately
$base64AuthInfo = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(("{0}:{1}" -f $user,$password)))
$headers = #{Authorization=("Basic {0}" -f $base64AuthInfo)}
$type = "application/json"
$url = "http://abc:8080/tfs/GlobalCollection/Project/_apis/build/builds?api-version=2.0"
$body = "{ 'definition' : { 'id' : 56} }"
Write-Host "URL: $url"
$definition = Invoke-RestMethod -Uri $url -Body $body -ContentType $type -Method Post -Headers $headers
Write-Host "Definition = $($definition | ConvertTo-Json -Depth 1000)"