Advent of Code is back! Unwrap daily challenges to sharpen your Alteryx skills and earn badges along the way! Learn more now.
Free Trial

Alteryx Server Discussions

Find answers, ask questions, and share expertise about Alteryx Server.
SOLVED

Migrate Workflows via Admin API Powershell

igallion
7 - Meteor

Does anyone have an example of migrating workflows between Alteryx servers with Powershell 5.1? Specifically, I'm looking for an example of how to format the Multipart/Form-Data request to upload a workflow to the destination server (the Post /admin/v1/workflows/ endpoint).

13 REPLIES 13
TimN
13 - Pulsar

Hi,

I've done this in Python in Alteryx release 2021.3.  I used Python code found here:

 

https://github.com/Theamazingdp/AlteryxGalleryAPI/blob/master/alteryx_gallery_api_v3_compatible.py

 

If you can translate to Powershell...

 

I added this to the above code to get the WF package

 

#api/admin/v1/{appID}/package/
def getAppAdmin(self, appId):
method = 'GET'
url = self.api_location + '/' + 'admin/v1/' + appId + '/package/'
params = self.build_oauth_params()
signature = self.generate_signature(method, url, params)
params.update({'oauth_signature': signature})
local_filename = 'D:\\Jupyter\\AlteryxAPI\\' + appId + '.yxzp'
# NOTE the stream=True parameter below
with requests.get(url, stream=True, params=params) as yxzp:
yxzp.raise_for_status()
with open(local_filename, 'wb') as f:
for chunk in yxzp.iter_content(chunk_size=8192):
if chunk: # filter out keep-alive new chunks
f.write(chunk)
# f.flush()

 

Hope that helps.

igallion
7 - Meteor

Thanks for the reply! I'm actually looking for advice on using the /admin/v1/workflows Post endpoint to upload a workflow to one Alteryx server from another. In my script I've successfully been able to check for migratable workflows, download the workflows and reset the migrate flag. What I'm having trouble with is formatting the request to upload the workflows to the new environment after they've been downloaded as a .yxzp file.

TimN
13 - Pulsar

Hi,

I'm pretty much at the same point as you.  I've gotten all endpoints to work in a Python script except this one.  My company is exploring automating implementation but we're just starting.  I did find this just now and it may help.

 

https://documenter.getpostman.com/view/14766220/TzeUmTWD#51daad84-b5ed-4977-bce1-03d8f09f7f7d

 

TimN_0-1667399257166.png

 

Regards,

 

Tim

 

igallion
7 - Meteor
Here is what I have so far for reference. When running this I get unauthorized and internal server errors:    
 
#Upload workflows to Prod environment
    $packages = Get-ChildItem $AlteryxMigrateFolder -Recurse -Include *.yxzp
    foreach ($package in $packages){
        $boundary = [System.Guid]::NewGuid().ToString()
        $fileBytes = [System.IO.File]::ReadAllBytes($package.FullName);
        $fileEnc = [System.Text.Encoding]::GetEncoding('UTF-8').GetString($fileBytes);
        $workflowName = $package.Name.Split(".")[0]
        $fileName = $package.Name
        $LF = "`r`n"
       
        $body = (
            "--$boundary",
            "Content-Disposition: form-data; name=`"file`"; filename=`"$fileName`"",
            "Content-Type: application/json$LF",
            $fileEnc,
            "--$boundary",
            "Content-Disposition: form-data; name=`"name`"$LF",
            $workflowName,
            "--$boundary",
            "Content-Disposition: form-data; name=`"owner`"$LF",
            "email@email.com",
            "--$boundary",
            "Content-Disposition: form-data; name=`"validate`"$LF",
            "false",
            "--$boundary",
            "Content-Disposition: form-data; name=`"isPublic`"$LF",
            "false",
            "--$boundary",
            "Content-Disposition: form-data; name=`"sourceId`"$LF",
            "",
            "--$boundary",
            "Content-Disposition: form-data; name=`"workerTag`"$LF",
            "",
            "--$boundary",
            "Content-Disposition: form-data; name=`"canDownload`"$LF",
            "false",
            "--$boundary--$LF"
        ) -join $LF
 
Invoke-RestMethod -Uri $finalUri -Method Post -ContentType "application/json; boundary=`"$boundary`"" -Body $body -Verbose
TimN
13 - Pulsar
 

We got it working in Postman.  We're Server 2021.3, I used Server Admin Key and Secret.  We Set SSL Certification to Off.  

 

TimN_2-1667416418729.png

 

igallion
7 - Meteor

I have solved this and in short - upgrade to version 2021.3+ and use the oauth 2.0 endpoints.

 

Authenticating using oauth 2.0 is infinitely simpler than oauth 1.0 and the v3 API endpoints add some great new features. Here's a simple example of using the v3 API to get a list of gallery collections:

 

$baseaddress = "https://YourServerName/webapi"

$tokenRequestBody = @{

        'client_id' = $apiKey
        'client_secret' = $apiSecret
        'grant_type' = 'client_credentials'
    }

$accessToken = invoke-restmethod -uri $baseaddress/oauth2/token -method POST -body $tokenRequestBody

 

 $headers = @{
     'Authorization' = 'Bearer ' + $accessToken
}
$collections = Invoke-RestMethod -Uri $baseaddress/v3/collections? -Method Get  -Headers $headers
 
Here is an example of how to upload a workflow using the v3/workflows POST endpoint. I used cURL instead of invoke-restmethod since powershell 5.0 does not natively support multipart/form-data. Using cURL is much easier than manually formatting for invoke-restmethod:
 
$uploadReq = .\curl.exe -s -k -X POST --header 'Content-Type: multipart/form-data' "https://YourServer/webapi/v3/workflows" --header 'Accept: application/json' --header "Authorization: Bearer $accessToken" `
--form "file=@$workflowFilePath" `
--form "name=$workflowName" `
--form "ownerId=$ownerID" `
--form 'isPublic="false"' `
--form 'isReadyForMigration="false"' `
--form 'othersMayDownload="true"' `
--form 'othersCanExecute="true"' `
--form "comments=$workflowComments" `
--form 'executionMode="Standard"'
 
#Print results (new workflow ID is returned if successful)
$uploadReq
 
JosephBih
5 - Atom

Thanks for this posting!

JosephBih
5 - Atom

I tried this way but it does not work for me - it keeps telling me "Invoke-RestMethod : The remote server returned an error: (401) Unauthorized."  

apathetichell
19 - Altair

@JosephBih question - for this part- 

$tokenRequestBody = @{

        'client_id' = $apiKey
        'client_secret' = $apiSecret
        'grant_type' = 'client_credentials'
    }
 
did you create powershell variables for your credentials?
you have to actually create variables for your $apiSecret and $apiKey - if these are not specific to your api - yes you will get a 401.