I have built a PUT Request using Invoke-RestMethod in a powershell script.
This request looks like :
# Main global var
$securePassword = ConvertTo-SecureString -String $pat -AsPlainText
$credential = [PSCredential]::new($username, $securePassword)
$organization = "myorganization"
$project = "myproject"
$username = "username"
$pat = "myRandomPatFromAzureDevOps"
# AZDO build env var
$buildNumber = "test1.0"
# AZDO Wiki global vars
$page = "Release-Notes-$buildNumber"
$Body = [PsCustomObject]@{
"content" = "$results"
} | ConvertTo-Json
$headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
$headers.Add("Content-Type", "application/json")
Invoke-RestMethod -Uri "https://dev.azure.com/$organization/$project/_apis/wiki/wikis/MyWiki.wiki/pages?path=$page&api-version=7.0" -Method 'PUT' -Authentication Basic -Credential $credential -Headers $headers -Body $Body
As we can see on the azure devops rest api in order to add content in the wiki repository here, in order to update the wiki I have to give in the Request Body a parameter named content into string format.
This the content of my var $results
pass to the content
parameter :
|Id|AreaPath|IterationPath|WorkItemType|State|Title|CloseDate|
|---------|---------------|--------------------|-------------------|------------|------------|--------------------------------|
|8888|Parts Unlimited Team|Parts Unlimited Sprint 2023-03|Bug|Closed|Parts Unlimited Sprint log security password|03-03-2023 13:31:37|
|9999|Parts Unlimited Team|Parts Unlimited Sprint 2023-03|Task|Closed|Parts Unlimited SprintAutomatisation APIs |03-01-2023 08:32:31|
As you can see, I am trying to pass a MarkDown table as a string. For format the result like this I have found and used a function named ConvertTo-MarkDownTable :
function ConvertTo-MarkDownTable {
[CmdletBinding()] param(
[Parameter(Mandatory = $true, ValueFromPipeline = $true, Position = 0)]
$InputObject
)
Begin {
$headersDone = $false
$pattern = '(?<!\\)\|' # escape every '|' unless already escaped
}
Process {
if (!$headersDone) {
$headersDone = $true
# output the header line and below that a dashed line
# -replace '(?<!\\)\|', '\|' escapes every '|' unless already escaped
"`n|{0}|" -f (($_.PSObject.Properties.Name -replace $pattern, '\|') -join '|')
"`n|{0}|" -f (($_.PSObject.Properties.Name -replace '.', '-') -join '|')
}
"`n|{0}|" -f (($_.PsObject.Properties.Value -replace $pattern, '\|') -join '|')
}
}
The initial data in $results
are values that I recovered from a GET Method of Azure DevOps Rest API. When I see the type of my $results
, I have this :
# $results.GetType()
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True Object[] System.Array
Now when I run the Invoke-RestMethod
, noting is sent in the body content parameter :
Invoke-RestMethod -Uri "https://dev.azure.com/$organization/$project/_apis/wiki/wikis/MyWiki.wiki/pages?path=$page&api-version=7.0" -Method 'PUT' -Authentication Basic -Credential $credential -Headers $headers -Body $Body
Output :
path : /path-to-push-content
order : 1
gitItemPath : /path%2Dto%2Dcontent.md
subPages : {}
url : https://dev.azure.com/myorganization/id-of-event-etag/_apis/wiki/wikis/fid-of-wiki-request/pages
/%2Fpath-to-push-content
remoteUrl : https://dev.azure.com/myorganization/id-of-event-etag/_wiki/wikis/fid-of-wiki-request?pagePath=%
2Fpath-to-push-content
id : randomId
content :
But I have tested this request using POSTMAN
. And everything is working well.
This is the request :
As you can see, the results is parsed and interpreted correctly.
And this is the upload on azure devops wiki :
Why my PUT method request does not sent content in my powershell script as POSTMAN do ?
Since you're passing a media type of application/json
:
You must pass a JSON string to Invoke-WebRequest
's -Body
parameter
$results
is an array of lines, you must manually join its elements with newlines to form a single, multi-line string: $results -join "`n"
. If you rely on implicit stringification inside an expandable string ("$results"
), the elements are joined with spaces instead.PowerShell will not automatically convert a [pscustomobject]
or hashtable to JSON for you.
Therefore, construct your $Body
variable as follows, using ConvertTo-Json
:
$Body = @{
"content" = $results -join "`n"
} | ConvertTo-Json
Note how passing a hashtable rather than a [pscustomobject]
is sufficient.
A general caveat (it doesn't apply here): More deeply nested objects / hashtables may get truncated, unless you pass a sufficiently high -Depth
value to ConvertTo-Json
- see this post.
Here's a self-contained example that demonstrates that a JSON string passed to -Body
is posted correctly:
data
property of the JSON response it returns.Invoke-RestMethod
instead of Invoke-WebRequest
automatically parses that JSON response into a [pscustomobject]
graph, which makes it easier to visualize the successful round trip.$results = @'
|Id|AreaPath|IterationPath|WorkItemType|State|Title|CloseDate|
|---------|---------------|--------------------|-------------------|------------|------------|--------------------------------|
|8888|Parts Unlimited Team|Parts Unlimited Sprint 2023-03|Bug|Closed|Parts Unlimited Sprint log security password|03-03-2023 13:31:37|
|9999|Parts Unlimited Team|Parts Unlimited Sprint 2023-03|Task|Closed|Parts Unlimited SprintAutomatisation APIs |03-01-2023 08:32:31|
'@
$body = @{
"content" = $results
} | ConvertTo-Json
(
Invoke-RestMethod https://postman-echo.com/put `
-Method PUT `
-ContentType application/json `
-Body $body
).data |
Format-List
Output, which shows that the $results
value was properly submitted:
content : |Id|AreaPath|IterationPath|WorkItemType|State|Title|CloseDate|
|---------|---------------|--------------------|-------------------|------------|------------|--------------------------------|
|8888|Parts Unlimited Team|Parts Unlimited Sprint 2023-03|Bug|Closed|Parts Unlimited Sprint log security password|03-03-2023 13:31:37|
|9999|Parts Unlimited Team|Parts Unlimited Sprint 2023-03|Task|Closed|Parts Unlimited SprintAutomatisation APIs |03-01-2023 08:32:31|