Skip to main content
Skip table of contents

Using PowerShell to automate the Runtime Process

“PowerShell is a modern command shell that includes the best features of other popular shells. Unlike most shells that only accept and return text, PowerShell accepts and returns .NET objects. This makes it a prime candidate for many developers and Dev Ops professionals looking to automate their workflows. “

Why use Powershell over the interface?

The Runtime interface strives to be easy in usage, and enables users to create and maintain their anonymization and subsetting workloads in any rapidly changing environment. So why use a scripting language to facilitate this over just manually uploading your projects and executing them?

Once Runtime has been configured, using the API to handle (parts) of Runtime’s execution can aid you in, for example:

  • Making repetitive tasks automatic. An easy example would be collecting information about past runs so that the right people in your organisation have access to it. Perhaps you’d like to mail it to a Data Steward, or automatically move it to a shared folder for review.

  • Integrating Runtime’s functions into your own software. Binding buttons in your own software to API scripts is relatively easy, and reduces the complexity of your own stack as long as tasks are predictable.

  • Reformatting Runtime’s data into your own preferred reports. We generate a lot of information, and parsing this all from the user interface can be tiring. Using the API you can pull large bulk amounts of information, and process it afterwards. This allows you to develop an even deeper understanding of your business processes.

Who is PowerShell development intended for?

Creating scripts in PowerShell is complex if you have little experience using it. Most APIs are designed to be easy to use for software engineers, and ours is (generally) quite easy to use and well-documented, but often creating lengthy scripts takes time and no small amount of troubleshooting. In order to get the most out of our API, you’ll need to have a deep understanding of the inherent possibilities within PowerShell, and how you can make Runtime communicate with other programs, utilizing variables to pass along information. Because PowerShell can instruct other software on your PC and perform OS level operations as well, the possibilities are wider than we could ever discuss here.

This guide isn’t a complete masterclass, but aims to give you the hands-on experience needed to get to grips with the documentation so you can read and use its instructions. Once you’ve mastered that, you should be able to build on that to create more complex scripts using our documentation and all the existing documentation about PowerShell.

What you need to start building scripts

In order to start making scripts, you need a few things:

  • A working installation of Runtime, and access to the user interface
  • The PowerShell ISE editor and curl (these are included with Windows, so you probably already have them!)
  • A valid API key for our REST-API. You can find this by generating one in the API tokens menu in Runtime

Getting started: Using the documentation

In order to use PowerShell, we need to know what kind of commands the API expects. After launching Runtime, navigate to the user interface. At the bottom left of the page, you should see API Docs.

Click me!

Once you do, you’ll be taken to a new local page in your browser that contains all the information on our API. This is our automatically generated documentation, made using ReDoc. Upon launching, you should be greeted by an overview of the various chapters. The documentation is broken up into chapters of functionality. Let’s start with looking at an easy command by retrieving a list of all the applications we’ve uploaded to Runtime.

  • Click on Applications in the left menu bar
  • Click on Retrieve a list of applications in the subsequently unfolded sub-menu.

You’ll now be scrolled down to the right command. You should see a screen which looks something like this:

All API requests have a short description letting you know what their function is!

On the left hand, we have the Query Parameters and the Responses. In the top-right corner we can see the Request address, and below that the Response samples. Let’s go over these one at a time.

Query parameters

Query parameters are values you can (and sometimes must!) supply to a command for it to function. Additionally, you can see which data type the value is. If a parameter is required and not optional, it will display required in red below the parameter name.

Responses

Any request we send to Runtime will get a response. The responses tab can be used to find out what data the specific HTTP response codes contain. If you’d like to learn more about what which code means, refer to the following article here. This allows you to get an idea of what to expect before executing a script. If a response item only has a few possible results to pick from (i.e.; picking from a list of the 7 major relational databases we support), it will display all possible items.

In this example, a HTTP code 200 (success) contains the applicationType as a result. Here, we can see that there’s only four possible types to pick from. If you ever have to specify an application type yourself, you can see here that these are the naming conventions we expect as input!

Response Samples

Sometimes, seeing the template of the response can be tedious to interpret. That’s why we’ve included a sample with some dummy values to show you what a response could look like, with added formatting. You can click in this box to unfold hidden items, and using the top buttons you can look at the various responses corresponding to different HTTP codes.

Arguably easier on the eyes than our response schemas!

Request Address

All requests need to be directed to a certain address. The address displayed here is a suffix, meaning that it is the extension that is added onto the back of the address runtime is hosted at. If you click on this bar, it will unfold, revealing the complete URL. You can copy this, and use it in your scripts.

Additionally, this bar displays the request method. In this example, this is a POST command. You will need to know this, as defining your method is required for sending requests to Runtime.

Building our first request

In order to create our first request, we’ll be using the Invoke-WebRequest command. This is PowerShell’s swiss army knife for interacting with all web-based content. Copy and paste the following command into PowerShell:

POWERSHELL
Template:
$runtimeURL = '<your_runtime_url_here>'
$apikey = '<your_api_key_here>'

$GetApplications = Invoke-WebRequest -Uri $runtimeUrl'/api/2/applications' -Headers @{"X-Auth-Token"="$apiKey"} -Method GET -ContentType "application/json" | ConvertFrom-Json

My example:
$runtimeURL = 'http://localhost:7070'
$apikey = '371943cd-1234-56789-8b92-64c77265cb8b'

$GetApplications = Invoke-WebRequest -Uri $runtimeUrl'/api/2/applications' -Headers @{"X-Auth-Token"="$apiKey"} -Method GET -ContentType "application/json" | ConvertFrom-Json

When executing the above, you should now see a neat oversight of your installed applications. Let’s disassemble this command and look at why we’ve done certain things.

Using variables to cut down on reentry work

Certain values need to be reused often. Our API key and the base URL of our Runtime will be reused in every command we send, so using the dollar sign to declare them as a variable saves us precious reentry time, and if we ever need to change these values, we only need to change them in one central location. Once you’ve set a variable, you can call it over and over again.

You’ll see that we opted to use $GetApplications as a variabel here to store the result of the following command. This is so that we can use the data we generate (the response we see in the console when normally executed.) later. We’ll be handling this later on.

POWERSHELL
Setting a variable value:
$MyVariable = 'Delicious!'

Executing a variable:
$MyVariable
----------------- 
PowerShell output: Delicious!

Supplying Invoke-Webrequest with parameters

The Invoke-Webrequest command can take many inputs, but we only need a few, depending on the command.

In the example above, our actual command looks something like this:

CODE
Invoke-WebRequest -Uri $runtimeUrl'/api/2/applications' -Headers @{"X-Auth-Token"="$apiKey"} -Method GET -ContentType "application/json" | ConvertFrom-Json

Looking at the documentation, we can find many aspects of what we needed here. We can get the URL and the fact that we need to use the GET method from the request address, and we know we don’t need to supply any extra information through the Query Parameters. There are two values which we cannot find, but luckily these are always static. The header for any command sent to our API must contain the API key under the name “X-Auth-Token”. The ContentType is usually “application/json”, but you can see this on the left-hand side in the API documentation under the Query Parameters.

If you don’t include an API key in the header, your request will not be processed and you will get a 401 - Unauthorized error! This ensures only authorized individuals are able to send commands to the application. This means you should restrict access to your API key and store it responsibly.

The API response

The API sends a response based on the command used, and if this contains a body with information it’ll be in JSON format. On its own, this can be a little hard to read. This is why we’ve included the “ConvertFrom-Json” command. This tells PowerShell that we’d like our response formatted nicely in a usable text format. This should look something like this:

Making the showcase project

Now that we know how to use PowerShell in combination with our API, we’re going to go forward and create a simple deployment script that can be iterated on. The following diagram describes the steps we’ll need to take to achieve this. From this point onward, every chapter will contain one of these steps. In subsequent chapters we’ll be using building on top of what we’ve created in the preceding step. This means that you must follow this guide in order, or risk not having variables used in the documentation.

If you ever get lost, at the very bottom of this page is a complete template where you can fill in the variables. All values in templates that need to be filled are indicated with “<>”.

Step one: Creating a new project

In order to start a run, we’ll need a group to store our environment in. In the API groups are known as projects. Navigating to the documentation, we can see that we need to provide only one thing; a name for our project.

Let’s start by making sure we have the variables we need available to us.

POWERSHELL
template:
$runtimeURL = '<your_runtime_url_here>'
$apikey = '<your_api_key_here>'

my example:
$runtimeURL = 'http://localhost:7070'
$apikey = '371943cd-1234-56789-8b92-64c77265cb8b'

Now, we need to supply a name. While we add this, let’s make sure we add comments to our query so that we’re able to reference back to the code later easily.

POWERSHELL
template:
$runtimeURL = '<your_runtime_url_here>'
$apikey = '<your_api_key_here>'

#Name variable for the group
$newProject = '{"name": "<your_group_name_here>"}'

#Creating the group
 try {
        $resProj = Invoke-WebRequest -Uri $runtimeURL'/api/2/projects' -Headers @{"X-Auth-Token"="$apiKey"} -Method POST -Body $newProject -ContentType "application/json" | ConvertFrom-Json
        Write-Host 'Successfully Created Group' $resProj.name
    }
    catch { Failure }

my example:
$runtimeURL = 'http://localhost:7070'
$apikey = '371943cd-123314-56789-8b9322-64c37712265c312b8b'

#Name variable for the group
$newProject = '{"name": "MyFavouriteGroup"}'

#Creating the group
 try {
        $resProj = Invoke-WebRequest -Uri $runtimeURL'/api/2/projects' -Headers @{"X-Auth-Token"="$apiKey"} -Method POST -Body $newProject -ContentType "application/json" | ConvertFrom-Json
        Write-Host 'Successfully Created Group' $resProj.name
    }
    catch { Failure }

If you execute this, you should be greeted by the following message in the console:

The reason we’re able to print the name of the group we’ve created is that the command we’ve just sent has a response. Because we stored the output JSON of our Invoke-WebRequest into a variable, we can output the values a success response yields. For this specific command, we can see that there are two values in the JSON that the API returns.

Because we input Write-Host 'Successfully Created Group' $resproj.name, not only will it write Successfully Created Group, it will also dive into the $resProj JSON response, and search for the value that matches the name column. Then, it will output these two values together as a string. You could also do this with the id column by using $resproj.id instead!

What's the “try {<your_request>} catch { Failure }” block, and why do we use it here?

The try-catch block enables us to execute whatever is in catch {} whenever the try {} block fails. In laymen’s terms this means that if our request is wrong or fails for some reason, we execute “failure”. This is just something we wrote in there to force the script to stop.

If you don’t include this block with your requests, it’s possible that the rest of the PowerShell script will keep executing even after this component fails. This leads to remnants and other artifacts which we’d like to avoid.

Step two: Installing an environment into our group

Now that we have a group, we can add a request on to our PowerShell script to also create an environment inside. Let’s look at the documentation to find out what we need.

Here, we can see an example of what the payload could look like. Let’s fill this in with some dummy values, making sure to turn data we might want to change or re-use into variables.

POWERSHELL
Template:
$runtimeURL = '<your_runtime_url_here>'
$apiKey = '<your_api_key_here>'

#Name variable for the group
$newProject = '{"name": "<your_project_name_here>"}'

#Name variable for the environment
$environmentName = "<your_environment_name_here>"


# Target Database Connection 
$targetConnection = '{\"type\":\"<your_database_type_here>\",\"method\":\"CONNECTION_PROPERTIES\",\"host\":\"<your_database_host_here>\",\"port\":\"<your_database_port_number_here>\",\"username\":\"<database_username_here>\",\"plaintextPassword\":\"<your_database_password_here>\",\"database\":\"<your_database_name_here>\"}'

    try {
        $resProj = Invoke-WebRequest -Uri $runtimeURL'/api/2/projects' -Headers @{"X-Auth-Token"="$apiKey"} -Method POST -Body $newProject -ContentType "application/json" | ConvertFrom-Json
        $projectId = $resProj.id
        Write-Host 'Successfully Created Group' $resProj.name
    }
    catch { Failure }
    
    # Create Runtime Environment
    
    $newEnvironment = '{"projectId": "'+$projectId+'", "name": "'+$environmentName+'", "type": "<your_database_type_here>", "parameters": [{"name": "DPF_TARGET_CONNECTION", "value": "'+$targetConnection+'", "type": "CONNECTIONSTRING" } ]}'
    try {
        $resEnv = Invoke-WebRequest -Uri $runtimeUrl'/api/2/environments' -Headers @{"X-Auth-Token"="$apiKey"} -Method POST -Body $newEnvironment -ContentType "application/json" | ConvertFrom-Json
        Write-Host 'Successfully Created Environment' $resEnv.name
    }
    catch { Failure }
    
My example: 
$runtimeURL = 'http://localhost:7070'
$apiKey = 'be3301s194-97saa5-4881sda236-89dsa4-041asdsad34416912098a'

#Name variable for the group
$newProject = '{"name": "MyFavouriteGroup"}'

#Name variable for the environment
$environmentName = "MyFavouriteMSSQLEnvironment"

# Target Database Connection 
$targetConnection = '{\"type\":\"MSSQL\",\"method\":\"CONNECTION_PROPERTIES\",\"host\":\"xxx-xxxxxxx-x\",\"port\":\"9999\",\"username\":\"fakeuser\",\"plaintextPassword\":\"fakepassword\",\"database\":\"fake_databasename\"}'

    try {
        $resProj = Invoke-WebRequest -Uri $runtimeURL'/api/2/projects' -Headers @{"X-Auth-Token"="$apiKey"} -Method POST -Body $newProject -ContentType "application/json" | ConvertFrom-Json
        $projectId = $resProj.id
        Write-Host 'Successfully Created Group' $resProj.name
    }
    catch { Failure }
    
    # Create Runtime Environment
    
    $newEnvironment = '{"projectId": "'+$projectId+'", "name": "'+$environmentName+'", "type": "MSSQL", "parameters": [{"name": "DPF_TARGET_CONNECTION", "value": "'+$targetConnection+'", "type": "CONNECTIONSTRING" } ]}'
    try {
        $resEnv = Invoke-WebRequest -Uri $runtimeUrl'/api/2/environments' -Headers @{"X-Auth-Token"="$apiKey"} -Method POST -Body $newEnvironment -ContentType "application/json" | ConvertFrom-Json
        Write-Host 'Successfully Created Environment' $resEnv.name
    }
    catch { Failure }

On successful execution, you should see the following, letting us know we were successful:

Figuring out the target connection data

Because of certain limitations, we need to send a JSON within another JSON to create an environment. Without proper annotation, this will cause the request to break, and fail to succeed. That’s why in the above example, we’ve had to use escape characters to make sure the API parses all the quotation marks as plain text.

Because there’s multiple database types, and not all databases require the same data, it’s important to tailor your $targetConnection to what your database connection requires. This are generally identical to what you would need to input in Privacy/Subset. Because this can be a source of confusion, I’ve included a cheat sheet you can use to see which values you need per database type, and what their name is.

Database Type

Required Connection Data

OracleDB

  • type (always ORACLE)

  • method (always CONNECTION_PROPERTIES)

  • host

  • port

  • username

  • plaintextPassword

  • service (in the Privacy/Subset UI you can specify whether it’s a service or SID, for the api the field is always named service )

SQL Server

  • type (always MSSQL)

  • method (always CONNECTION_PROPERTIES)

  • host

  • port

  • username

  • plaintextPassword

  • database

IBM For DB2 iSeries/LUW

  • type (always DB2_LUW or DB2_I depending on which you use)

  • method (always CONNECTION_PROPERTIES)

  • host

  • port

  • username

  • plaintextPassword

  • database

PostgreSQL

  • type (always PGSQL)

  • method (always CONNECTION_PROPERTIES)

  • host

  • port

  • username

  • plaintextPassword

  • database

Step Three: Uploading an application

In order to execute a run, we’ll need to have an application uploaded to Runtime for us to install into our environment. Looking at the documentation, we can see the following:

we can see that we only need to supply a file. However, we can also see that instead of the application/json form type we’re used to, this request needs to be a multipart/form-data. Currently, using PowerShell to supply form-data doesn’t seem to work well. Instead of being able to supply a file path to our application ZIP, we would need to load the entire file into memory before sending it.

This is technically possible, but for this tutorial I’ll elect to use curl from within PowerShell instead, as the syntax is a lot easier to reproduce, and we’d like to reduce complexity where we can.

Let’s take a look at the code I’ve written to upload a package.

POWERSHELL
Template:  
    #Upload an application zip file

    try {
    $resApp = curl.exe -F file=@"$applicationPackage"  --header "X-auth-token: $apiKey"  $runtimeURL'/api/2/applications'  -X POST -s | ConvertFrom-Json
        Write-Host 'Successfully Uploaded Application'$resApp.name 'with ID' $resApp.id 
    }
    catch { Failure }
    
    
My example: 
    #Upload an application zip file

    try {
    $resApp = curl.exe -F file=@"$applicationPackage"  --header "X-auth-token: $apiKey"  $runtimeURL'/api/2/applications'  -X POST -s | ConvertFrom-Json
        Write-Host 'Successfully Uploaded Application'$resApp.name 'with ID' $resApp.id 
    }
    catch { Failure }
    

In order to execute curl through PowerShell, simply input curl.exe at the start of your command.

After executing this query, you should see a message indicating the success of all three steps, and the return message of the uploaded application. This is a JSON file which contains metadata information. This template works, but it’s important to realize that because we enter a system path to our application zip file, and run the script locally it’s entirely possible that if someone else were to run your script locally, they are unable to access the file location.

To verify that the application was uploaded successfully you can navigate to the Applications menu in the Runtime user interface.

Always make sure to store files used in your scripts in a location which are accessible by everyone whom you intend to let use the script!

Step four: installing our application

Now that our application has been uploaded into Runtime, we can install it into the environment we’ve created. Earlier we’ve used $resEnv.id to pull the ID value from the response we got from the API after creating an environment, and we’ve stored it into the variable $environmentId. This is a variable we’ll be needing for the following request, as we need to know which environment we need to install our application into.

We also need to know what the ID of the application we’ve just uploaded is, and we did this with $applicationId = $resApp.id (our variable applicationId is the ID value from variable $resApp, which is the response from our curl command)

Let’s look at the documentation:

On the left-hand side in the documentation for this request we can see that only an environmentId and applicationId are required, and we’ve made sure to store those into variables during our last step. This is the command I’ve made for this step:

POWERSHELL
   Template:
  
   #Installing our application file into our environment
     try {
        $newInstallation = '{ "applicationId":"'+$applicationId+'" , "environmentId":"'+$environmentId+'"}'
        $resInstall = Invoke-WebRequest -Uri $runtimeURL'/api/2/installations'  -Headers @{"X-Auth-Token"="$apiKey"} -Method POST -Body $newInstallation -ContentType "application/json" | ConvertFrom-Json
        Write-Host 'Successfully Installed Application' $resApp.name 'with ID' $applicationId 
    }
    catch { Failure }
   
   My example:
   #Installing our application file into our environment
     try {
        $newInstallation = '{ "applicationId":"'+$applicationId+'" , "environmentId":"'+$environmentId+'"}'
        $resInstall = Invoke-WebRequest -Uri $runtimeURL'/api/2/installations'  -Headers @{"X-Auth-Token"="be330194-97a5-4886-89d4-04144169098a"} -Method POST -Body $newInstallation -ContentType "application/json" | ConvertFrom-Json
        Write-Host 'Successfully Installed Application' $resApp.name 'with ID' $applicationId 
    }
    catch { Failure }

Step Five: Starting a run

Now that we’ve made an entirely configured environment, we can start our run! For this step, you have two options:

  • Creating a PowerShell script manually, using the documentation

  • Generating an API call through the Runtime UI.

Because quite often the primary use of our API is to start runs quickly, our user interface can generate a simple API request to start any run. This is a nice quality-of-life feature that allows end-users to introduce a little automation without needing any knowledge of the API. Let’s take a look!

  • Open the Runtime user interface

  • Navigate to your chosen group & open the desired environment.

  • You should now see your installed applications. Select the application which you’d like to start

  • Click Start…

  • You should now see the following screen after switching from the Deploy now to the API usage tab:

Here, you can copy the PowerShell query, and execute it in your own script. The only change you need to make is replace <TOKEN_HERE> with your API token. This functionality removes the need for you to search your installationId through your scripts, which, for a static environment, is a nice feature.

Looking at the documentation, we can see that we need to supply the environmentId

Let’s take a look at the code I’ve written.

CODE
Template:

# Starting a new run using our installed application
    
    try {
        Write-Host  'Starting run of application' $resApp.name 
        $resRun = Invoke-WebRequest -Uri $runtimeURL'/api/2/runs?installationId='$installationId  -Headers @{"X-Auth-Token"="$apiKey"} -Method POST -Body '{"scenarioName":"<enter_your_scenario_here>"}' ` -ContentType "application/json"   | ConvertFrom-Json 
        Write-Host  'Successfully started run with ID' $installationId
   } 
   catch { Failure }     
   
My example: 
# Starting a new run using our installed application
    
    try {
        Write-Host  'Starting run of application' $resApp.name 
        $resRun = Invoke-WebRequest -Uri $runtimeURL'/api/2/runs?installationId='$installationId  -Headers @{"X-Auth-Token"="$apiKey"} -Method POST -Body '{"scenarioName":"anonymize"}' ` -ContentType "application/json"   | ConvertFrom-Json 
        Write-Host  'Successfully started run with ID' $installationId
   } 
   catch { Failure }     

What’s next?

We’ve gone through the steps to create an execution of a template from start to finish, but there is so much more our API and PowerShell is capable of! Some ideas:

  • Storing and moving information about our run locally or through e-mail
  • Altering existing parameters within Runtime
  • Integrating Runtime into your own environment
  • Monitoring the current status of runs
  • Handling errors elegantly
  • Using Runtime data in external parsers (logs, audit data and other information)

By now, you should be well equipped to interpret the Runtime API documentation, and perform basic scripting tasks with PowerShell. In order to further test and improve integration, we encourage you to study the API documentation and experiment with small, iterative improvements to your scripts. At the bottom of this page is a complete template of the run described in this chapter which you can reference at any time. You’ll see that I’ve added slight changes such as better spacing of responses to make our output more readable, but the core functionality should be the same as what you’ve made when following this guide.

Attachment: the full script template

CODE
Template:

$runtimeURL = '<your_runtime_url_here>'
$apikey = '<your_api_key_here>'

#Name variable for the group
$newProject = '{"name": "<your_group_name_here>"}'

#Name variable for the environment
$environmentName = "<your_environment_name_here>"


$targetConnection = '{\"type\":\"<your_database_type_here>\",\"method\":\"CONNECTION_PROPERTIES\",\"host\":\"<your_database_host_here>\",\"port\":\"<your_database_port_number_here>\",\"username\":\"<database_username_here>\",\"plaintextPassword\":\"<your_database_password_here>\",\"database\":\"<your_database_name_here>\"}'# Our application zip file to upload
$applicationPackage = '"<your_full_system_path_to_application_here>"'

        Write-Host '---------------------------------------------------------------------' 
        Write-Host 'STARTING UP AUTOMATED RUNTIME DEPLOYMENT' 
        Write-Host '---------------------------------------------------------------------' 

    try {
        $resProj = Invoke-WebRequest -Uri $runtimeURL'/api/2/projects' -Headers @{"X-Auth-Token"="$apiKey"} -Method POST -Body $newProject -ContentType "application/json" | ConvertFrom-Json
        $projectId = $resProj.id
        Write-Host 'Successfully Created Group' $resProj.name 'with ID' $resProj.id
        Write-Host '---------------------------------------------------------------------' 
    }
    catch { Failure }
    
    # Create Runtime Environment
    
    $newEnvironment = '{"projectId": "'+$projectId+'", "name": "'+$environmentName+'", "type": "<your_database_type_here>", "parameters": [{"name": "DPF_TARGET_CONNECTION", "value": "'+$targetConnection+'", "type": "CONNECTIONSTRING" } ]}'
    try {
        $resEnv = Invoke-WebRequest -Uri $runtimeUrl'/api/2/environments' -Headers @{"X-Auth-Token"="$apiKey"} -Method POST -Body $newEnvironment -ContentType "application/json" | ConvertFrom-Json
        Write-Host 'Successfully Created Environment' $resEnv.name 'with ID' $resEnv.id
        Write-Host '---------------------------------------------------------------------' 
    }
    catch { Failure }

   #Store environment ID for later use
    $environmentId = $resEnv.id

    #Upload an application zip file

    try {
    $resApp = curl.exe -F file=@"$applicationPackage"  --header "X-auth-token: $apiKey"  $runtimeURL'/api/2/applications'  -X POST -s | ConvertFrom-Json
        Write-Host 'Successfully Uploaded Application'$resApp.name 'with ID' $resApp.id 
        Write-Host '---------------------------------------------------------------------' 
    }
    catch { Failure }

   #Store application ID for later use

    $applicationId = $resApp.id

   # some quick tests to make sure variables were actually being set and I'm not hallucinating
   # Write-Host $resApp.id 'newvar' $applicationId ' test '  $environmentId
    
    #Installing our application file into our environment
     try {
        $newInstallation = '{ "applicationId":"'+$applicationId+'" , "environmentId":"'+$environmentId+'"}'
        $resInstall = Invoke-WebRequest -Uri $runtimeURL'/api/2/installations'  -Headers @{"X-Auth-Token"="$apiKey"} -Method POST -Body $newInstallation -ContentType "application/json" | ConvertFrom-Json
        
   #Store Installation ID for later use
   $installationId = $resInstall.installationId

        Write-Host 'Successfully Installed Application' $resApp.name 'with ID' $installationId 
        Write-Host '---------------------------------------------------------------------'  
    }
    catch { Failure }


# Starting a new run using our installed application
    
    try {
        Write-Host  'Starting run of application' $resApp.name 
        Write-Host '---------------------------------------------------------------------'
        $resRun = Invoke-WebRequest -Uri $runtimeURL'/api/2/runs?installationId='$installationId  -Headers @{"X-Auth-Token"="$apiKey"} -Method POST -Body '{"scenarioName":"anonymize"}' ` -ContentType "application/json"   | ConvertFrom-Json 
        Write-Host  'Successfully started run with ID' $installationId
        Write-Host '---------------------------------------------------------------------'
   } 
   catch { Failure }     
    
JavaScript errors detected

Please note, these errors can depend on your browser setup.

If this problem persists, please contact our support.