Search code examples
powershellinvoke-webrequest

Converting a Python HTTP POST to Powershell: how do I send mixed data?


I've got a Python script which I'm trying to convert to Powershell. The script uploads a file to Amazon S3. Along with the file, it has to send some data (the request_attibutes dict). This data gets filled from a previous request, I've got some placeholder data here.

request_attibutes = {
    'AWSAccessKeyId': 'ID',
    'key': 'ldskfjhasdf',
    'x-amz-security-token': 'adsfsdfjkl',
    'policy': 'akjsdfh',
    'signature': 'askjdhf'}

http_response = requests.post(url, data=request_attibutes, files=files)

Converted to Powershell:

$request_attibutes = @{AWSAccessKeyId=$keyid; key=$document_upload_data.key; 'x-amz-security-token'= $document_upload_data.'x-amz-security-token'; policy= $document_upload_data.policy;signature= $document_upload_data.signature}
$cf=Get-Item -Path $contentfile

I'm using an array to pack the $request_attibutes and the file into a single item.

$ra=@($request_attibutes2,$cf)

$response2 = Invoke-WebRequest -Uri $uploadurl -Method Post -Body $ra

The server responds with (412) Precondition Failed. So there's something the server doesn't expect. But what?


Solution

  • I ended up building the request body by hand:

    $boundary = [System.Guid]::NewGuid().ToString();
    $LF = "`r`n";
    $bodyLines = (
        "--$boundary",
        "Content-Disposition: form-data; name=`"key`";",
      "Content-Type: text$LF",
        "$contentuploadpath",
        "--$boundary",
        "Content-Disposition: form-data; name=`"AWSAccessKeyId`";",
      "Content-Type: text$LF",
        $document_upload_data.awsaccesskeyid,
        "--$boundary",
        "Content-Disposition: form-data; name=`"policy`";",
      "Content-Type: text$LF",
        $document_upload_data.policy,
        "--$boundary",
        "Content-Disposition: form-data; name=`"signature`";",
      "Content-Type: text$LF",
        $document_upload_data.signature,
        "--$boundary",
        "Content-Disposition: form-data; name=`"x-amz-security-token`";",
      "Content-Type: text$LF",
        $document_upload_data.'x-amz-security-token',
        "--$boundary",
        "Content-Disposition: form-data; name=`"file`";     filename=`"$contentfilename`"",
      "Content-Type: application/octet-stream$LF",
        $fileEnc,
        "--$boundary--$LF"
     ) -join $LF
    

    That resulted in an output that looked identical to the Python script I was trying to replicate, but I still got server errors on the upload.

    So I switched from Invoke-WebRequest to Invoke-RestMethod:

    Invoke-RestMethod -Uri $uploadurl -Method Post -ContentType "multipart/form-data; boundary=`"$boundary`"" -Body $bodyLines
    

    Now it works, unfortunately in PS 5 Invoke-RestMethod does not return the server response so I'm uploading blindly. At the server we can see the data coming in though.