CURL - Powershell : Invoke-RestMethod to GLPI API : Upload Document - powershell

I would like to upload a file to my GLPI from API.
This curl command works fine :
curl -X POST
-H 'Content-Type: multipart/form-data'
-H 'Session-Token: $sessiontoken'
-H 'App-Token:$apptoken'
-F 'uploadManifest={"input": {"name": "Uploaded document", "_filename" : ["file.txt"]}};type=application/json'
-F 'filename[0]=#file.txt' 'http://GLPI_SERVER/glpi/apirest.php/Document'
The same curl in powershell (Thank's to Jean-Christophe from GLPI) :
$DocumentUpload = C:\curl\bin\curl.exe `
-X POST `
-H 'Content-Type: multipart/form-data' `
-H "Session-Token: $($SessionToken.session_token)" `
-H 'App-Token: '$AppToken `
-F 'uploadManifest={\"input\": {\"name\": \"Document ticket 161\", \"_filename\" : [\"clear.png\"], \"tickets_id\":\"161\"}};type=application/json' `
-F 'filename[0]=#"C:\temp1\clear.png"' `
-s `
-k 'https://glpi.xxxx.fr/apirest.php/Document/'
I would like use the Invoke-RestMethod but without success
# GLPI REST API CONFIG :
$AppURL = "https://glpi.xxxxxxxxxxxx.fr/apirest.php"
$UserToken = "zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz"
$AppToken = "XXXXXXXXXXX"
$ID = 161
$ItemType = "Ticket"
$SubItemType = "Document"
$manifest = #{}
$SessionToken = Invoke-RestMethod "$AppURL/initSession" -Method Get -Headers #{"Content-Type" = "application/json";"Authorization" = "user_token $UserToken";"App-Token"=$AppToken}
$FileContent = [System.IO.File]::ReadAllBytes("C:\temp1\clear.png")
$input = New-Object System.Collections.ArrayList
$type =New-Object System.Collections.ArrayList
$filename =New-Object System.Collections.ArrayList
$input = #{input=#{"name"="titre";"_filename"="clear.png";"tickets_id"="161"}}
$type = #{"type"="application/json"}
$filename =#{"filename[0]"="C:\temp1\clear.png"}
$manifest.Add("uploadManifest",$input)
$manifest.Add("type=","application/json")
$manifest.Add("filename[0]=",#("C:\temp1\clear.png"))
$json = #($manifest | ConvertTo-Json)
#Display Convert to JSON
#$json
Invoke-RestMethod "$AppURL/$($ItemType)/$($ID)/$($SubItemType)" -Method Post -Headers #{"session-token"=$SessionToken.session_token; "App-Token" = $AppToken} -Body ([System.Text.Encoding]::UTF8.GetBytes($json)) -ContentType 'multipart/data'
## Test autre ContentType
Invoke-RestMethod "$AppURL/$($ItemType)/$($ID)/$($SubItemType)" -Method Post -Headers #{"session-token"=$SessionToken.session_token; "App-Token" = $AppToken} -Body ([System.Text.Encoding]::UTF8.GetBytes($json)) -ContentType 'multipart/form-data'
#Kill session
Invoke-RestMethod "$AppURL/killSession" -Headers #{"session-token"=$SessionToken.session_token; "App-Token" = "$AppToken"}
The Invoke with : -ContentType 'multipart/data'
["ERROR_BAD_ARRAY","The input parameter must be an array of objects
The Invoke with : -ContentType 'multipart/form-data'
["ERROR_UPLOAD_FILE_TOO_BIG_POST_MAX_SIZE","The file seems too big"]
I thank in advance if someone sees my mistakes and help me.
Same topic to : How convert curl file upload command to Invoke-RestMethod?

press f12 in chrome to open devtools, go to network tab (probably good idea to enable XHR filter too)
manually upload file - new requests will show up in devtools
find the one that uploads file
right click - copy - as powershell
that will copy entire request that you can then adjust/review to have better idea of what goes where

Thank's for your answer Charg, but when i try there are lot of change and pages. I have try this option before post here :D. Because for me the copy is not useable. Maybe easiest for you :
I have to create a new reply because the comment does not allow enough characters.
(i have deleted headers)
Invoke-WebRequest -Uri "https://glpi.XXXXXXXXXXXX.fr/ajax/fileupload.php" `
-Method "POST" `
-Headers #{
} `
-ContentType "multipart/form-data; boundary=----WebKitFormBoundarysgETZNBiXoMdeht2Q" `
-Body ([System.Text.Encoding]::UTF8.GetBytes("------WebKitFormBoundarysgETZNBiXoMdeht2Q$([char]13)$([char]10)Content-Disposition: form-data; name=`"name`"$([char]13)$([char]10)$([char]13)$([char]10)filename$([char]13)$([char]10)------WebKitFormBoundarysgETZNBiXoMeht2Q$([char]13)$([char]10)Content-Disposition: form-data; name=`"showfilesize`"$([char]13)$([char]10)$([char]13)$([char]10)1$([char]13)$([char]10)------WebKitFormBoundarysgETZNBiXoMeht2Q$([char]13)$([char]10)Content-Disposition: form-data; name=`"filename[]`"; filename=`"apple.png`"$([char]13)$([char]10)Content-Type: image/png$([char]13)$([char]10)$([char]13)$([char]10)$([char]13)$([char]10)------WebKitFormBoundarysgETZNBiXoMeht2Q--$([char]13)$([char]10)"))
And other body (more informations) :
-Body ([System.Text.Encoding]::UTF8.GetBytes("------WebKitFormBoundarymPxXKSWBW70lLtX2 $([char]13)$([char]10)Content-Disposition: form-data; name=`"documentcategories_id`"$([char]13)$([char]10)$([char]13)$([char]10)10$([char]13)$([char]10)------WebKitFormBoundarymPxXKSWBW70lLtX2$([char]13)$([char]10)Content-Disposition: form-data; name=`"entities_id`"$([char]13)$([char]10)$([char]13)$([char]10)2$([char]13)$([char]10)------WebKitFormBoundarymPxXKSWBW70lLtX2$([char]13)$([char]10)Content-Disposition: form-data; name=`"is_recursive`"$([char]13)$([char]10)$([char]13)$([char]10)0$([char]13)$([char]10)------WebKitFormBoundarymPxXKSWBW70lLtX2$([char]13)$([char]10)Content-Disposition: form-data; name=`"itemtype`"$([char]13)$([char]10)$([char]13)$([char]10)Ticket$([char]13)$([char]10)------WebKitFormBoundarymPxXKSWBW70lLtX2$([char]13)$([char]10)Content-Disposition: form-data; name=`"items_id`"$([char]13)$([char]10)$([char]13)$([char]10)161$([char]13)$([char]10)------WebKitFormBoundarymPxXKSWBW70lLtX2$([char]13)$([char]10)Content-Disposition: form-data; name=`"tickets_id`"$([char]13)$([char]10)$([char]13)$([char]10)161$([char]13)$([char]10)------WebKitFormBoundarymPxXKSWBW70lLtX2$([char]13)$([char]10)Content-Disposition: form-data; name=`"_filename[0]`"$([char]13)$([char]10)$([char]13)$([char]10)6075d0cb639f08.69946676apple.png$([char]13)$([char]10)------WebKitFormBoundarymPxXKSWBW70lLtX2$([char]13)$([char]10)Content-Disposition: form-data; name=`"_prefix_filename[0]`"$([char]13)$([char]10)$([char]13)$([char]10)6075d0cb639f08.69946676$([char]13)$([char]10)------WebKitFormBoundarymPxXKSWBW70lLtX2$([char]13)$([char]10)Content-Disposition: form-data; name=`"_tag_filename[0]`"$([char]13)$([char]10)$([char]13)$([char]10)ea6f951c-f37490bd-6075d0cb7fc703.58112843$([char]13)$([char]10)------WebKitFormBoundarymPxXKSWBW70lLtX2$([char]13)$([char]10)Content-Disposition: form-data; name=`"filename[]`"; filename=`"`"$([char]13)$([char]10)Content-Type: application/octet-stream$([char]13)$([char]10)$([char]13)$([char]10)$([char]13)$([char]10)------WebKitFormBoundarymPxXKSWBW70lLtX2$([char]13)$([char]10)Content-Disposition: form-data; name=`"add`"$([char]13)$([char]10)$([char]13)$([char]10)Ajouter un nouveau fichier$([char]13)$([char]10)------WebKitFormBoundarymPxXKSWBW70lLtX2$([char]13)$([char]10)Content-Disposition: form-data; name=`"_glpi_csrf_token`"$([char]13)$([char]10)$([char]13)$([char]10)583040be8c019c6b482ff61d4c6c37230109cb37876001d086603c1b4a144eb0$([char]13)$([char]10)------WebKitFormBoundarymPxXKSWBW70lLtX2--$([char]13)$([char]10)"))
And I found a guy, who wrote this part in Python (maybe help to translate to powershell ?)
multipart_form_data = {
'uploadManifest': (None, '{"input": {"name": "test123.png", "_filename": ["test123.png"]}}'),
'file': ('test123.png', open('test123.png', 'rb')),
}
And one other guy find his solution in C# (i have losted the post id)
// Upload
var RSClient = new RestClient(Properties.Settings.Default.GLPI_URL);
var request = new RestRequest("Document", Method.POST);
request.AddHeader("Session-Token", Properties.Settings.Default.GLPI_SESSION_TOKEN);
request.AddHeader("App-Token", Properties.Settings.Default.GLPI_APP_TOKEN);
request.AddHeader("Accept", "application/json");
request.AddHeader("Content-Type", "multipart/form-data");
request.AddQueryParameter("uploadManifest", "{\"input\": {\"name\": \"UploadFileTest\", \"_filename\": \"GiletsJaunes.jpg\"}}");
request.AddFile("test", #"C:\path\to\File.jpg");
IRestResponse response = RSClient.Execute(request);
var content = response.Content;
textBox2.Text = textBox2.Text + Environment.NewLine + content;

Related

Making a PowerShell invoke-restmethod request

I have been challenged to get a response from an api with powershell.
I have little to no experience with powershell, but have at least been able to get a response from the API provider, but not a response i can use, so i hope someone can help me.
The photo is of the documentaion i have
Documentation
What i can't figure out is how i send the message that i want a response for.
The command is the specific api that i need to use, and it has some inputs as seen in the photo.
Input to API:
StreetName
BuildingIdentifier
Floor
Suite
DistrictCode
This is what i got so far.
$uri = 'SomeUrl.com'
$command = '/Ois/RealUnit/Address'
$token = 'SomeSecureToken'
$contentType = 'application/json'
$secureToken = ConvertTo-SecureString $token -AsPlainText -force
$webResult = Invoke-RestMethod -Method get -Uri $uri -ContentType $contentType -Token $secureToken
write-output $webResult
What i know but i don't know how to use or i it has to be used is as follows.
Request URL: https://SomeUrl.com/Ois/RealUnit/Address
Response body: is JSON and i only need the response for "unitUse"
Response Code: I also know that a good response gives the code 200
Curl: I also know that something called CURL looks a bit like this curl -X POST --header 'Content-Type: application/x-www-form-urlencoded' --header 'Accept: application/json' -d 'StreetName=STREET&BuildingIdentifier=123&Floor=12&Suite=th&DistrictCode=1234' 'https://SomeUrl.com/Ois/RealUnit/Address'
Reponse Header: Looks like this
{ "access-control-allow-headers": "Content-Type", "access-control-allow-methods": "GET, POST, PUT, DELETE, PATCH, OPTIONS", "access-control-allow-origin": "*", "cache-control": "private", "content-length": "7604", "content-type": "application/json; charset=utf-8", "date": "Tue, 08 Mar 2022 13:21:27 GMT", "server": "Microsoft-IIS/8.5", "strict-transport-security": "max-age=31536000", "vary": "Accept", "x-aspnet-version": "4.0.30319", "x-powered-by": "ServiceStack/5.10 NET45 Win32NT/.NET, ASP.NET" }
$uri = 'https://SomeUrl.com/Ois/RealUnit/Address'
$token = 'SomeSecureToken'
$contentType = 'application/json'
$secureToken = ConvertTo-SecureString $token -AsPlainText -force
$Body = #{
StreetName = "STREET"
BuildingIdentifier = 123
Floor = 12
Suite = "th"
DistrictCode = 1234
}
$webResult = Invoke-RestMethod -Method POST -Uri $uri -ContentType $contentType -Token $secureToken -Body $Body
write-output $webResult
I can't really remember if you have to convert body to JSON data or if the 'Invoke-RestMethod' does it automatically. In any case: this is how you could do it:
$webResult = Invoke-RestMethod -Method POST -Uri $uri -ContentType $contentType -Token $secureToken -Body ($Body | ConvertTo-Json)

Curl To Powershell - Click Send

I am struggling with this code as I have never encountered
Here is the Example code that click send provides.
curl --include \
--header "Authorization: Basic YXBpLXVzZXJuYW1lOmFwaS1wYXNzd29yZA==" \
--request POST \
--header "Content-Type: application/x-www-form-urlencoded" \
--data-binary "username=myusername&key=1234-I3U2RN34IU-43UNG&to=61411111111,64122222222,61433333333&senderid=example&message=testing" \
'https://api-mapper.clicksend.com/http/v2/send.php'
URL: https://developers.clicksend.com/docs/http/v2/#send-an-sms
Here is what I have figured out so far:
$BaseURL = "https://api-mapper.clicksend.com/http/v2/send.php"
$Header = #{
"Authorization" = "Basic"+[System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes("$APIKey"))
}
$body = #{
"useranme"="$Username"
"key"="$APIKey"
"to"="$To"
"senderid"="$from"
"message"=$Message
}
$Return = Invoke-RestMethod -Method Post -Headers $Header -ContentType "application/x-www-form-urlencoded" -Uri $BaseURL -Body $body
$return.InnerXml
I'm stuck on the --data-binary part of the code.
I believe using the --data-binary in curl is like creating a string in the body of the request:
$BaseURL = "https://api-mapper.clicksend.com/http/v2/send.php"
$Username = "Username"
$APIKey = "SomeAPIKey"
$To = "Recipient"
$From = "Sender"
$Message = "MessageFoRecipient"
$Header = #{
"Authorization" = "Basic"+ " " + ([System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes("$APIKey")))
}
$body = "username=$Username" + "&" + "key=$APIKey" + "&" + "to=$To" + "&" + "senderid=$From" + "&" + "message=$Message"
$return = Invoke-RestMethod -Method Post -Headers $Header -ContentType "application/x-www-form-urlencoded" -Uri $BaseURL -Body $body
$return.InnerXml
Invoke-RestMethod assumes that you want your POST body to be application/x-www-form-urlencoded (unless otherwise specified, e.g. XML, JSON).  You must be having an issue with something such as the fact that the Authorization header doesn't have a space between Basic and the Base64 string, or the fact that username has a typo.
$BaseURL = 'http://localhost:8000'
$Username = 'api-username'
$ApiKey = 'api-password'
$To = '61411111111,64122222222,61433333333'
$From = 'example'
$Message = 'testing'
$SenderId = 'example'
$Body = #{
username = $Username
key = $ApiKey
to = $To
senderid = $SenderId
message = $Message
}
$Header = #{
Authorization = 'Basic ' + ([System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes("$APIKey")))
}
$Return = Invoke-RestMethod -Method Post -Headers $Header -Uri $BaseURL -Body $Body
$Return.InnerXml
Yields the following HTTP request:
POST / HTTP/1.1
Host: localhost:8000
Authorization: Basic YXBpLXBhc3N3b3Jk
User-Agent: Mozilla/5.0 (Windows NT 10.0; Microsoft Windows 10.0.19042; en-US) PowerShell/7.2.1
Content-Length: 112
Content-Type: application/x-www-form-urlencoded
key=api-password&message=testing&username=api-username&to=61411111111%2C64122222222%2C61433333333&sender=example

Invoke-RestMethod Upload ZIP File

I try to upload a file to a uri with POST. I somehow fail. I got help from
powershell invoke-restmethod multipart/form-data
but they uploaded a text file.
Also the curl command works:
curl -X POST "https://some.place.over.the.rainbow.com/api/v1/dataupdate" -H "accept: */*" -H "Authorization: Basic 123123123123" -F "file=#/c/updates/SomeFile.zip"
In my ps1 Script I tried following:
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
$Headers = #{
Authorization = "Basic 123123123123"
};
$FilePath = "C:\Users\asd\SomeFile.zip";
$Uri = 'ttps://some.place.over.the.rainbow.com/api/v1/dataupdate';
$LF = "`r`n";
$boundary = [System.Guid]::NewGuid().ToString();
$fileBytes = [System.IO.File]::ReadAllBytes($FilePath);
$fileEnc = [System.Text.Encoding]::GetEncoding('UTF-8').GetString($fileBytes);
$bodyLines = (
"--$boundary",
"Content-Disposition: form-data; name=`"file`"; filename=`"SomeFile.zip`"",
$fileEnc,
"--$boundary--$LF"
) -join $LF
Invoke-RestMethod -Uri $Uri -Method Post -ContentType "multipart/form-data; boundary=`"$boundary`"" -Headers $Headers -Body $bodyLines
On Serverside I get
Servlet.service() for servlet [dispatcherServlet] in context with path [/...] threw exception [Request processing failed; nested exception is
org.springframework.web.multipart.MultipartException: Failed to parse multipart servlet request;
nested exception is java.io.IOException: org.apache.tomcat.util.http.fileupload.FileUploadException:
Header section has more than 10240 bytes (maybe it is not properly terminated)] with root cause
org.apache.tomcat.util.http.fileupload.MultipartStream$MalformedStreamException: Header section has more than 10240 bytes (maybe it is not properly terminated)at
org.apache.tomcat.util.http.fileupload.MultipartStream.readHeaders(MultipartStream.java:523)
~[tomcat-embed-core-8.5.34.jar!/:8.5.34]at org.apache.tomcat.util.http.fileupload.FileUploadBase$FileItemIteratorImpl.findNextItem(FileUploadBase.java:880)
~[tomcat-embed-core-8.5.34.jar!/:8.5.34]at org.apache.tomcat.util.http.fileupload.FileUploadBase$FileItemIteratorImpl.<init>(FileUploadBase.java:845)
I need to execute it on a windows machine where I do not haver curl unfortunately.
Appreciate any help

Upload document to Salesforce using powershell

I'm trying to upload a text file in our Salesforce application using PowerShell.
The curl code which I'm trying to mimic in PowerShell is:
curl -X POST -H "Authorization: {SESSION_ID}" \
-H "Content-Type: multipart/form-data" \
-F file=#document.txt \
-F "name__v=myDocument" \
-F "type__v=Undefined" \
-F "lifecycle__v=Unclassified" \
https://{server}/api/{version}/objects/documents
Here is the PowerShell code which I have written taking hint from the web:
$URL="https://{server}/api/{version}/auth"
$CT="application/x-www-form-urlencoded"
$User="user"
$Password="pass"
$SessionID=(Invoke-RestMethod -Method Post -Uri $URL -Body #{"Content-Type" = $CT; "username" = $User; "password" = $Password}).sessionId
$URL="https://{server}/api/{version}/objects/documents"
$boundary=[System.Guid]::NewGuid().ToString()
$LF="`r`n"
$File=Get-Content("....\test.txt")
$fileBin=[System.IO.File]::ReadAllBytes("....\test.txt")
$enc=[System.Text.Encoding]::GetEncoding("iso-8859-1")
$fileEnc=$enc.GetString($fileBin)
$Body1=(
"--$boundary",
"Content-Disposition: form-data; name__v=`"kaz.txt`"; type__v=`"Undefined`"; lifecycle__v=`"Unclassified`"",
"Content-Type: application/octet-stream$LF",
$fileEnc,
"--$boundary--$LF"
) -join $LF
$Body2 = (
"file=$File; name__v=`"kaz.txt`"; type__v=`"Undefined`"; lifecycle__v=`"Unclassified`""
)
Invoke-RestMethod -Method POST -Uri $URL -Headers #{"Authorization" = $SessionID; "Content-Type" = "multipart/form-data; boundary=`"$boundary`""} -Body $Body1
Invoke-RestMethod -Method POST -Uri $URL -Headers #{"Authorization" = $SessionID; "Content-Type" = "multipart/form-data; boundary=`"$boundary`""} -Body $Body2
None of these are working and I'm getting error
PARAMETER_REQUIRED lifecycle__v: Missing required parameter : lifecycle__v.
I modified the body and now its working fine
$Body = (
"--$boundary", "Content-Disposition: form-data; name=`"file`"; filename=`"test.docx`"", "Content-Type: application/octet-stream$LF", $fileEnc,
"--$boundary", "Content-Disposition: form-data; name=`"lifecycle__v`"", "", "Unclassified",
"--$boundary", "Content-Disposition: form-data; name=`"type__v`"", "", "Undefined",
"--$boundary--"
)-join $LF
Invoke-RestMethod -Method POST -Uri $URL -Headers #{"Authorization" = $SessionID; "Content-Type" = "multipart/form-data; boundary=$boundary"} -Body $Body

Using PowerShell Invoke-RestMethod to POST large binary multipart/form-data

I'm trying to use the Invoke-RestMethod cmdlet in PowerShell 3 and 4, to upload a large binary file using a REST API's multipart/form-data upload. Here is a working cURL example on how to perform what I want to do in PowerShell:
curl -i -k -H "accept: application/json" -H "content-type: multipart/form-data" -H "accept-language: en-us" -H "auth: tokenid" -F file="#Z:\large_binary_file.bin" -X POST "https://server/rest/uri2"
I would love to see a working example on how to use Invoke-RestMethod to POST a multipart/form-data. I found a blog post from the PowerShell team showing how to use Invoke-RestMethod to upload to OneDrive (aka SkyDrive), but doesn't work well. I'd also like to avoid using System.Net.WebClient if at all possible. I also found another thread here on Stackoverflow, but it really didn't help much.
[System.Net.ServicePointManager]::ServerCertificateValidationCallback = { $true }
$server = "https://server"
uri = "/rest/uri1"
$headers = #{"accept" = "application/json"; "content-type" = "application/json";"accept-language" = "en-us"}
$body = #{"userName" = "administrator"; "password" = "password"}
$method = "POST"
#Get Session ID
$resp = Invoke-RestMethod -Method $method -Headers $headers -Uri ($server+$uri) -body (convertto-json $Body -depth 99)
$sessionID = $resp.sessionID
#Upload file
$uri = "/rest/uri2"
$headers = #{"accept" = "application/json";"content-type" = "multipart/form-data"; "accept- language" = "en-us"; "auth" = $sessionID}
$medthod = "POST"
$largeFile = "Z:\large_binary_file.bin"
I have tried both ways of using Invoke-RestMethod:
Invoke-RestMethod -Method $method -Headers $headers -Uri ($server+$uri) -InFile $largeFile
or
$body = "file=$(get-content $updateFile -Enc Byte -raw)"
Invoke-RestMethod -Method $method -Headers $headers -Uri ($server+$uri) -body $body
I notice couple of mistakes in your invoke statement. First, you need to use POST keyword instead of string value. Second, make sure that Content-Type is set to multipart/form-data. So this is my revised statement -
$uri = "http://blahblah.com"
$imagePath = "c:/justarandompic.jpg"
$upload= Invoke-RestMethod -Uri $uri -Method Post -InFile $imagePath -ContentType 'multipart/form-data'
You can check the response from the server by checking $upload.