My powershell invoke-restmethod keeps giving the error: (415) Unsupported Media Type
This is my script:
add-type -AssemblyName System.Net.Http
$url = 'https://staging.tiptrack.nl/Tiptrack.Employer.Api/odata/Employers/UploadIncrementalEmployeeImport'
$accesstoken = $token_tiptrack.access_token
$filePath = 'C:/afas/Tiptrack_gebruikers.csv'
$fileName = 'Tiptrack_gebruikers.csv'
$header = #{
Authorization="Bearer 1235405sglhfdsgnsg;lsfnsljgflsdjgs'gresgsdfjlaskdf"
}
$content = New-Object System.Net.Http.MultipartFormDataContent
$fileStream = [System.IO.File]::OpenRead($filePath)
$fileName = [System.IO.Path]::GetFileName($filePath)
$fileContent = New-Object System.Net.Http.StreamContent($fileStream)
$fileContent.Headers.ContentType = New-Object System.Net.Http.Headers.MediaTypeHeaderValue 'text/csv'
$content.Add($fileContent, 'file', $fileName)
Invoke-RestMethod -Uri $url -Method Post -Headers $header -Body $content
Hope you can help me.
What version of PowerShell ($PSVersionTable.PSVersion)? The ability to use Invoke-RestMethod with a MultipartFormDataContent instance was only added in PowerShell 6, which is still not the default on most systems.
Related
I am having an issue related to the encoding.
$usermailactivitydetail = Invoke-RestMethod -uri $uri -headers $headers -ContentType "text/plain; charset=utf-8"
$usermailactivitydetail variable :
İç test01 Bölge)
https://learn.microsoft.com/en-us/graph/api/reportroot-getemailactivityuserdetail?view=graph-rest-1.0
script:
$appid = 'APPID'
$tenantid = 'TENANTID'
$secret = 'SECRETID'
$body = #{
Grant_Type = "client_credentials"
Scope = "https://graph.microsoft.com/.default"
Client_Id = $appid
Client_Secret = $secret
}
$ConnectionParameters=#{
Uri="https://login.microsoftonline.com/$tenantid/oauth2/v2.0/token"
Method ="POST"
Body = $body
}
$connection = Invoke-RestMethod #ConnectionParameters
$token = $connection.access_token
Connect-MgGraph -AccessToken $token
$headers = #{"Authorization" = "Bearer "+ $token}
$uri = "https://graph.microsoft.com/v1.0/reports/getEmailActivityUserDetail(period='D7')"
thanks,
UPDATE :
$usermailactivitydetail = Invoke-RestMethod -uri $uri -headers $headers
$result = $usermailactivitydetail.Replace('Report Refresh Date','Report Refresh Date')
$resultarray = ConvertFrom-Csv -InputObject $result
#Export result to CSV
$resultarray | Export-Csv "C:\output.csv" -NoTypeInformation -encoding utf8
As Jeroen suggested, try to download to a file to prevent Invoke-RestMethod from trying to interpret the data and get the encoding wrong. The file will contain the raw response from the server. Then force the encoding when reading the file through PowerShell.
$tempFilePath = (New-TemporaryFile).Fullname
Invoke-RestMethod -uri $uri -headers $headers -OutFile $tempFilePath
$usermailactivitydetail = Import-Csv $tempFilePath -Encoding UTF8
Remove-Item $tempFilePath
Using Postman the API call works. I used their code snippet feature to get the PowerShell equivalent. But when I try it on PowerShell ISE I get "Empty request body not allowed". Why does it think the body is empty? How can I confirm/look at the contents of $body?
using assembly System.Net.Http
using namespace System.Net.Http
$headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
$headers.Add("Authorization", "Bearer token")
$multipartContent = [System.Net.Http.MultipartFormDataContent]::new()
$multipartFile = 'C:\Users\xxx\Desktop\msg.json'
$FileStream = [System.IO.FileStream]::new($multipartFile, [System.IO.FileMode]::Open)
$fileHeader = [System.Net.Http.Headers.ContentDispositionHeaderValue]::new("form-data")
$fileHeader.Name = "ParameterRequest"
$fileContent = [System.Net.Http.StreamContent]::new($FileStream)
$fileContent.Headers.ContentDisposition = $fileHeader
$multipartContent.Add($fileContent)
$FileStream.Flush()
$FileStream.Close()
$multipartFile = 'C:\Users\xxx\Desktop\edm_payload'
$FileStream = [System.IO.FileStream]::new($multipartFile, [System.IO.FileMode]::Open)
$fileHeader = [System.Net.Http.Headers.ContentDispositionHeaderValue]::new("form-data")
$fileHeader.Name = "MessagePayload"
$fileContent = [System.Net.Http.StreamContent]::new($FileStream)
$fileContent.Headers.ContentDisposition = $fileHeader
$multipartContent.Add($fileContent)
$FileStream.Flush()
$FileStream.Close()
$body = $multipartContent
$response = Invoke-RestMethod -Method Post -Uri 'https://APIsite' -Headers $headers -Body $body -ContentType "multipart/form-data"
When I look at the contents of $body this is what I get. How can I see the data in each Content?
PS C:\Users\user> $body
Headers
-------
{[Content-Disposition, System.String[]]}
{[Content-Disposition, System.String[]]}
Added -Verbose to the Invoke-RestMethod and I see
VERBOSE: POST https://APIsite with -1-byte payload
I cannot find what is the meaning of "-1-byte payload". Anybody knows?
Solved. Part of the problem was that I was using PowerShell v5, the MultipartFormDataContent has been improved since then. Updated to version 7 of PowerShell, made a few tweaks... Success!
$formContent = New-Object -TypeName 'System.Net.Http.MultipartFormDataContent'
$filePath = "C:\Users\xxx\Desktop\msg.json"
$fileStream1 = [System.IO.File]::Open($filePath, [System.IO.FileMode]::Open, [System.IO.FileAccess]::Read)
$formContent.Add([System.Net.Http.StreamContent]::new($fileStream1), "ParameterRequest", (Split-Path $filePath -leaf))
$filePath = "C:\Users\xxx\Desktop\edm_payload"
$fileStream2 = [System.IO.File]::Open($filePath, [System.IO.FileMode]::Open, [System.IO.FileAccess]::Read)
$formContent.Add([System.Net.Http.StreamContent]::new($fileStream2), "MessagePayload", (Split-Path $filePath -leaf))
Invoke-RestMethod -Method Post -Uri 'https://APIsite' -Headers $requestHeader -Body $formContent -ContentType 'multipart/form-data'
Also - close your streams after the API call
I have been trying to pull out Azure Audit logs using Microsoft Graph. I am using Powershell to do this nut I can't get filtering to work.
For example, this URL returns lots of older data than then date specified in the filter.
https://graph.microsoft.com/v1.0/auditLogs/directoryAudits?\$filter=activityDateTime ge 2020-02-24T18:39:36Z
Here is my code
$ClientID = "yyyyyy"
$ClientSecret = "xxxxxxxxx"
$loginURL = "https://login.microsoftonline.com"
$tenantdomain = "xxx.onmicrosoft.com"
$resource = "https://graph.microsoft.com"
$Date = Get-Date
$UTC = $Date.ToUniversalTime();
$UTC
$Time = "{0:s}" -f $UTC.AddMinutes(-5) + "Z"
$Time
$body =
#{grant_type="client_credentials";resource=$resource;client_id=$ClientID;client_secret=$ClientSecret}
$oauth = Invoke-RestMethod -Method Post -Uri $loginURL/$tenantdomain/oauth2/token?api-
version=1.0 -Body $body
$headerParams = #{'Authorization'="$($oauth.token_type) $($oauth.access_token)"}
$url = 'https://graph.microsoft.com/v1.0/auditLogs/directoryAudits?\`$filter=activityDateTime ge ' +
$Time
$Data = Invoke-WebRequest -UseBasicParsing -Headers $headerParams -Uri $url
$DataFromJson = $Data.Content | ConvertFrom-Json
$ValueFromJason = $DataFromJson.value
$AllAuditData += $ValueFromJason
# Output
$Time
2020-02-24T18:39:36Z
$short = $AllAuditData | Sort-Object activityDateTime
$short[1].activityDateTime
2020-02-24T17:52:09.9673372Z # Old data as comparison to $Time
$short[-1].activityDateTime
2020-02-24T18:44:15.1283452Z
According to my test, we can use the following script
$ClientID = "yyyyyy"
$ClientSecret = "xxxxxxxxx"
$loginURL = "https://login.microsoftonline.com"
$tenantdomain = "xxx.onmicrosoft.com"
$resource = "https://graph.microsoft.com"
$body =
#{grant_type="client_credentials";resource=$resource;client_id=$ClientID;client_secret=$ClientSecret}
$oauth = Invoke-RestMethod -Method Post -Uri $loginURL/$tenantdomain/oauth2/token?api-
version=1.0 -Body $body
$Date = Get-Date
$UTC = $Date.ToUniversalTime();
$Time = "{0:s}" -f $UTC.AddMinutes(-5) + "Z"
$Time
$url= "https://graph.microsoft.com/beta/auditLogs/directoryAudits?`$filter=activityDateTime gt " + $Time
$headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
$headers.Add("Authorization", "$($oauth.token_type) $($oauth.access_token)")
$AllAuditData=#()
$response = Invoke-RestMethod $url -Method 'GET' -Headers $headers
$AllAuditData += $response.value
$short = $AllAuditData | Sort-Object activityDateTime
$short[1].activityDateTime
$short[-1].activityDateTime
Update
According to my research, we can use the Azure AD PowerShell command Get-AzureADAuditDirectoryLogs to filter audit logs. For more details, please refer to https://learn.microsoft.com/en-us/azure/active-directory/reports-monitoring/reference-powershell-reporting.
For example
Install-module AzureADPreview
Connect-AzureAD
$result=Get-AzureADAuditDirectoryLogs -Filter "activityDisplayName eq 'Update user'"
$result | Select-Object ActivityDisplayName
I have a Powershell code that is work very fine in powershell version 3.
I need to run this code in powershell 2.0 too. But Invoke-WebRequest not supported in PS version 2.0.
Please help me!
$params = "metrics[]=failed:count"
$failed = (Invoke-WebRequest -Uri http://localhost:9000/stats -Method POST -Body $params -ContentType "application/json").Content
$x = $failed | ConvertFrom-Json
Untested, but I think this may help:
$params = "metrics[]=failed:count"
$result = #{}
try{
$request = [System.Net.WebRequest]::Create('http://localhost:9000/stats')
$request.Method = 'POST'
$request.ContentType = 'application/json'
$request.Accept = "application/json"
$body = [byte[]][char[]]$params
$upload = $request.GetRequestStream()
$upload.Write($body, 0, $body.Length)
$upload.Flush()
$upload.Close()
$response = $request.GetResponse()
$stream = $response.GetResponseStream()
$streamReader = [System.IO.StreamReader]($stream)
$result['StatusCode'] = $response.StatusCode
$result['StatusDescription'] = $response.StatusDescription
$result['Content'] = $streamReader.ReadToEnd()
$streamReader.Close()
$response.Close()
}
catch{
throw
}
# I suggest checking $result.StatusCode here first..
$x = $result.Content | ConvertFrom-Json
I'm trying to send a file via Invoke-RestMethod in a similar context as curl with the -F switch.
Curl Example
curl -F FileName=#"/path-to-file.name" "https://uri-to-post"
In powershell, I've tried something like this:
$uri = "https://uri-to-post"
$contentType = "multipart/form-data"
$body = #{
"FileName" = Get-Content($filePath) -Raw
}
Invoke-WebRequest -Uri $uri -Method Post -ContentType $contentType -Body $body
}
If I check fiddler I see that the body contains the raw binary data, but I get a 200 response back showing no payload has been sent.
I've also tried to use the -InFile parameter with no luck.
I've seen a number of examples using a .net class, but was trying to keep this simple with the newer Powershell 3 commands.
Does anyone have any guidance or experience making this work?
The accepted answer won't do a multipart/form-data request, but rather a application/x-www-form-urlencoded request forcing the Content-Type header to a value that the body does not contain.
One way to send a multipart/form-data formatted request with PowerShell is:
$ErrorActionPreference = 'Stop'
$fieldName = 'file'
$filePath = 'C:\Temp\test.pdf'
$url = 'http://posttestserver.com/post.php'
Try {
Add-Type -AssemblyName 'System.Net.Http'
$client = New-Object System.Net.Http.HttpClient
$content = New-Object System.Net.Http.MultipartFormDataContent
$fileStream = [System.IO.File]::OpenRead($filePath)
$fileName = [System.IO.Path]::GetFileName($filePath)
$fileContent = New-Object System.Net.Http.StreamContent($fileStream)
$content.Add($fileContent, $fieldName, $fileName)
$result = $client.PostAsync($url, $content).Result
$result.EnsureSuccessStatusCode()
}
Catch {
Write-Error $_
exit 1
}
Finally {
if ($client -ne $null) { $client.Dispose() }
if ($content -ne $null) { $content.Dispose() }
if ($fileStream -ne $null) { $fileStream.Dispose() }
if ($fileContent -ne $null) { $fileContent.Dispose() }
}
The problem here was what the API required some additional parameters. Initial request required some parameters to accept raw content and specify filename/size. After setting that and getting back proper link to submit, I was able to use:
Invoke-RestMethod -Uri $uri -Method Post -InFile $filePath -ContentType "multipart/form-data"
I found this post and changed it a bit
$fileName = "..."
$uri = "..."
$currentPath = Convert-Path .
$filePath="$currentPath\$fileName"
$fileBin = [System.IO.File]::ReadAlltext($filePath)
$boundary = [System.Guid]::NewGuid().ToString()
$LF = "`r`n"
$bodyLines = (
"--$boundary",
"Content-Disposition: form-data; name=`"file`"; filename=`"$fileName`"",
"Content-Type: application/octet-stream$LF",
$fileBin,
"--$boundary--$LF"
) -join $LF
Invoke-RestMethod -Uri $uri -Method Post -ContentType "multipart/form-data; boundary=`"$boundary`"" -Body $bodyLines
For anyone wondering (like Jelphy) whether David's answer can be used with cookies/credentials, the answer is yes.
First set the session with Invoke-WebRequest:
Invoke-WebRequest -Uri "$LoginUri" -Method Get -SessionVariable 'Session'
Then POST to the Login URL, which stores the authentication cookie in $Session:
$Response = Invoke-WebRequest -Uri "$Uri" -Method Post -Body $Body -WebSession $Session
The steps above are the standard way to deal with session in Powershell. But here is the important part. Before creating the HttpClient, create an HttpClientHandler and set it's CookieContainer property with the cookies from the session:
$ClientMessageHandler = New-Object System.Net.Http.HttpClientHandler
$ClientMessageHandler.CookieContainer = $Session.Cookies
Then pass this object to the HttpClient constructor
$Client = [System.Net.Http.HttpClient]::new($ClientMessageHandler)
Voila, you now have an HttpClient with session cookies set automatically via Invoke-WebRequest. The rest of David's example should work (copied here for completeness):
$MultipartFormData = New-Object System.Net.Http.MultipartFormDataContent
$FileStream = [System.IO.File]::OpenRead($FilePath)
$FileName = [System.IO.Path]::GetFileName($FilePath)
$FileContent = New-Object System.Net.Http.StreamContent($FileStream)
$MultipartFormData.Add($FileContent, $FieldName, $FileName)
$Result = $Client.PostAsync($url, $content).Result
$Result.EnsureSuccessStatusCode()
I had many files to upload with each request, so I factored out this last bit into a lambda function:
function Add-FormFile {
param ([string]$Path, [string]$Name)
if ($Path -ne "")
{
$FileStream = [System.IO.File]::OpenRead($Path)
$FileName = [System.IO.Path]::GetFileName($Path)
$FileContent = [System.Net.Http.StreamContent]::new($FileStream)
$MultipartFormData.Add($FileContent, $Name, $FileName)
}
}