6 Replies Latest reply on Oct 6, 2017 3:53 AM by Zeeshan Zuberi

    REST API File Upload with PowerShell

    Chuck Schmidt

      I am developing a Powershell-based deployment model for our Tableau environment utilizing the Tableau Server REST API (Server v10; API version 2.3). I am able to login to server, login to sites, query projects, query workbooks, query data sources, and upload file, but encounter an error when attempting to publish a previously uploaded file:

      <error code="400011"><summary>Bad Request</summary><detail>There was a problem publishing the file '10356:81CF357C252F4EA19FE7CE27F9F6B3FD-0:0'.</detail></error>

       

      This same file upload succeeds when I execute the Python sample code provided, so I know the .twbx file is good, and the Tableau Server REST API is functioning. Using Fiddler, I have compared line-by-line the PowerShell POST request and Python POST request, and cannot see any differences. I'm not sure what I'm missing. Using PowerShell is a requirement.

       

      1. Has anyone successfully published a workbook with the REST API and PowerShell? If so, what's your secret to success?

      2. Does Tableau Server have API logs that provide more details than the useless error that is returned?

       

      Thanks for any assistance you can provide.

        • 1. Re: REST API File Upload with PowerShell
          Glen Robinson

          Hi Chuck

          I have been through the very same issue.

          It seems that using the Powershell Invoke-RestMethod (and Invoke-WebRequest) works fine with text based requests, but does something weird when uploading a twbx, tdsx, or tde.

           

          Therefore I used the System.New.WebClient method instead

           

          The setup is the same as with the Invoke-RestMethod, but using the following process.

           

            $wc = New-Object System.Net.WebClient

            $wc.Headers.Add('X-Tableau-Auth',$headers.Values[0])

            $wc.Headers.Add('ContentLength', $request_body.Length)

            $wc.Headers.Add('Content-Type', 'multipart/mixed; boundary=6691a87289ac461bab2c945741f136e6')

            $response = $wc.UploadString($url ,'POST', $request_body)

           

          The $request_body is of the following format

           

          Screen Shot 2016-09-27 at 19.22.47.png

           

          the boundary=xxxx needs to be the same as the boundary-string in the request_body

          the $url is of the format "http://servername/api/2.2/sites/<siteID>/workbooks"

           

          Hope this helps

           

          All the best

          Glen

          1 of 1 people found this helpful
          • 2. Re: REST API File Upload with PowerShell
            Chuck Schmidt

            Thank you for the reply and suggestion Glen. I tried the WebClient method, but that did not resolve my issue.

             

            My upload is now working. My issue was not with the "Publish Workbook" API call (which was failing), rather with the preceding "File Upload" API call (which was succeeding). I am using Invoke-RestMethod.

            • 3. Re: REST API File Upload with PowerShell
              Glen Robinson

              Thanks Chuck

              Thats interesting, as I could never get the Invoke-RestMethod to work (for either 'publish workbook' or 'File Upload')

              Is this with workbooks that contain extracts (always failed for me) or for workbooks without extracts (no problem getting this to work)?

              All the best

              Glen

              • 4. Re: REST API File Upload with PowerShell
                Chuck Schmidt

                With extract. Haven’t uploaded without extract yet – suppose I should test that too

                • 5. Re: REST API File Upload with PowerShell
                  Josh Thornes

                  Can you post a code snippet of this process.

                  • 6. Re: REST API File Upload with PowerShell
                    Zeeshan Zuberi

                    Would anyone care to share a working code snippet for uploading a simple .twb file that is using a published data source on Tableau server or point out what I could be doing wrong in my code snippet shared towards the end of this message? The file is less than 2MB in size so no need to do chunking etc.  I've tried tinkering with code I have and it just keeps throwing the following error:

                    <?xml version='1.0' encoding='UTF-8'?>

                    <tsResponse xmlns="http://tableau.com/api" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://tableau.com/api http://tableau.com/api/ts-api-2.5.xsd">

                        <error code="400000">

                            <summary>Bad Request</summary>

                            <detail>Payload is either malformed XML/JSON or incomplete</detail>

                        </error>

                    </tsResponse>

                     

                     

                    Here is the code snippet that I'm using which also shows commented lines reflecting what other variations I've tried but still no success:

                    ############################################################################################################################################

                    $TargetProjectName = "TestProject"
                    $Credential = Get-Credential
                    $TargetProjectID =
                    $TargetWorkbookName = "TestUpload"
                    $authToken = "AssumeCorrectAuthTokenIsSetHere"

                     

                    #Get contents of file read in a variable
                    #$content = [IO.File]::ReadAllText("C:\Tableau\TestUpload.twb")
                    #$content = [IO.File]::ReadAllBytes("C:\Tableau\TestUpload.twb")

                    $content = Get-Content C:\Tableau\TestUpload.twb -Encoding Byte

                     

                    $url = "https://myTableauServer.net/api/2.5/sites/$siteID/workbooks"

                     

                    #Create request body
                    $request_body = ('--my123BoundaryStringIsThis
                    Content-Disposition: name="request_payload"
                    Content-Type: text/xml

                    <tsRequest>
                      <workbook name="'+$TargetWorkbookName+'" showTabs="true">
                        <connectionCredentials name="' + $Credential.GetNetworkCredential().UserName + '" password="'+ $Credential.GetNetworkCredential().Password + '" embed="true" />
                        <project id="' + $TargetProjectID +'"/>
                      </workbook>
                    </tsRequest>
                    --my123BoundaryStringIsThis
                    Content-Disposition: name="' + $TargetWorkbookName + '"; filename="TestUpload.twb"
                    Content-Type: application/octet-stream

                     

                    '+$content+'
                    --my123BoundaryStringIsThis--')

                     

                    $newHeader = New-Object “System.Collections.Generic.Dictionary[[String],[String]]”
                    # add X-Tableau-Auth header with our auth token
                    $newHeader.Add('X-Tableau-Auth', $authToken)
                    $newHeader.Add('ContentLength', $request_body.Length)
                    $newHeader.Add('Content-Type', 'multipart/mixed; boundary=my123BoundaryStringIsThis')

                     

                    #$wc = New-Object System.Net.WebClient
                    #$wc.Headers.Add('X-Tableau-Auth',$authToken)
                    #$wc.Headers.Add('ContentLength', $request_body.Length)
                    #$wc.Headers.Add('Content-Type', 'multipart/mixed; boundary=my123BoundaryStringIsThis')

                     

                    $response = Invoke-RestMethod -Uri $url -Headers $newHeader -Body $request_body -Method Post
                    #$response = $wc.UploadString($url ,"post", $request_body)

                    ##########################################################################################################################