Tutorials

Use PowerShell to Consume a ProfitBricks REST API

Table of Contents

Introduction

The ProfitBricks REST API provides access to most of the functions that are available in the Data Center Designer (DCD). Several tools and libraries are available to ease consumption of the REST API. What if you want to directly access the API from PowerShell? This tutorial will demonstrate how to get started by accessing the API and performing a few basic operations.

Requirements

We will be using the Invoke-RestMethod cmdlet which was introduced in Windows PowerShell 3.0. Therefore we need access to a Windows installation with PowerShell 3.0 or newer. PowerShell 3.0 should be available on Windows 8 and Server 2012 by default. It can be installed on some earlier versions of Windows and Windows Server.

Verify the version of PowerShell by examining the output of $psversiontable. This is an example of the ouput from PowerShell running on a Windows 10 installation:

PS> $psversiontable

Name                           Value
----                           -----
PSVersion                      5.0.10586.122
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0...}
BuildVersion                   10.0.10586.122
CLRVersion                     4.0.30319.42000
WSManStackVersion              3.0
PSRemotingProtocolVersion      2.3
SerializationVersion           1.1.0.1

The PSVersion shows as "5.0..." so we will have Invoke-RestMethod available.

Authentication

The REST API needs to know who we are so it can determine if we have permission to perform whatever operations we are requesting of it. This authorization is handled by passing a Base64-encoded string containing your credentials in the header of each request. The first part of this string contains your username, which is generally an e-mail address (username@domain.tld). It is separated from the password by a colon. Here is a code snippet which demonstrates how to generate the Base64 encoded string from inside PowerShell.

$CredentialsText = ‘username@domain.tld:password’
$CredentialsBytes = [System.Text.Encoding]::UTF8.GetBytes($CredentialsText)
$EncodedCredentials = [Convert]::ToBase64String($CredentialsBytes)
$EncodedCredentials
dXNlcm5hbWVAZG9tYWluLnRsZDpwYXNzd29yZA==

Make a GET request

Lets go ahead and ask for some information about the data centers we have provisioned under our ProfitBricks account. First we will build the headers for the request:

$headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
$headers.Add("Authorization", 'Basic dXNlcm5hbWVAZG9tYWluLnRsZDpwYXNzd29yZA==')

or, if you still have the $EncodedCredentials variable available:

$headers.Add("Authorization", "Basic $EncodedCredentials")

Now we can make the GET request using the Invoke-RestMethod cmdlet:

$response = Invoke-RestMethod 'https://api.profitbricks.com/rest/v2/datacenters' -Headers $headers

We have stored the results of the request in a PowerShell object ($response) and we can use various cmdlets to view and manipulate the object.

PS C:\> $response | fl

id    : datacenters
type  : collection
href  : https://api.profitbricks.com/rest/v2/datacenters
items : {@{id=[one-data-center-id]; type=datacenter; href=https://api.profitbricks.com/rest/v2/datacenters/[one-data-center-id]},
         @{id=[two-data-center-id]; type=datacenter; href=https://api.profitbricks.com/rest/v2/datacenters/[two-data-center-id]},
         @{id=[three-data-center-id]; type=datacenter; href=https://api.profitbricks.com/rest/v2/datacenters/[three-data-center-id]},
         @{id=[four-data-center-id]; type=datacenter; href=https://api.profitbricks.com/rest/v2/datacenters/[four-data-center-id]}}

If we want to pick out a specific data center from the result set:

PS C:\> $response.items[0] | fl

id   : [one-data-center-id]
type : datacenter
href : https://api.profitbricks.com/rest/v2/datacenters/[one-data-center-id]

And one more level down:

PS C:\> $response.items[0].href
https://api.profitbricks.com/rest/v2/datacenters/[one-data-center-id]

Many of the requests that we will make to the REST API are scoped to a specific data center. The value returned above could be used as a base URL when composing additional requests.

Make a POST request

Getting information about existing data center objects is nice, but what if we wanted to create something new? This is generally accomplished by making a POST request with a JSON body containing the details of the object we wish to create.

Let's demonstrate this by adding a new data center. Looking at the REST API Data Center documentation, we discover the POST request requires a "Content-Type" and we also see an example of the JSON request body. We will reuse the $headers we created above for this request. First we will build the JSON request string:

$CreateDc = '{"properties":{"name": "Example DC", "description": "An Example DC", "location":"us/las"}}'

Next we will submit the request with the additional "Content-Type" added:

$dcPostRequest = Invoke-RestMethod 'https://api.profitbricks.com/rest/v2/datacenters' -Method Post -Headers $headers -Body $CreateDc -ContentType 'application/vnd.profitbricks.resource+json'

Our returned object, $dcPostRequest, contains information about the request we made:

PS C:\> $dcPostRequest | fl

id         : [new-data-center-id]
type       : datacenter
href       : https://api.profitbricks.com/rest/v2/datacenters/[new-data-center-id]
metadata   : @{createdDate=[date-timestamp]; createdBy=username@domain.tld; etag=[etag];lastModifiedDate=[date-timestamp];lastModifiedBy=username@domain.tld; state=BUSY}
properties : @{name=Example DC; description=An Example DC; location=us/las; version=; features=System.Object[]}

Make a PATCH Request

Our new data center has been provisioned, but we have decided that the description was not set correctly. A PATCH request is one way to make a change to an existing data center object. PATCH requests have a different "Content-Type" but are similar to POST requests. We will go ahead and update the data center description. First we need to compose the new request string:

$UpdateDc = '{"description": "The Example DC"}'

Now we will submit the request using the correct "Content-Type" and a new target URL containing our newly-created data center ID:

$dcPatchRequest = Invoke-RestMethod 'https://api.profitbricks.com/rest/v2/datacenters/[new-data-center-id]' -Method Patch -Headers $headers -Body $UpdateDc -ContentType 'application/vnd.profitbricks.partial-properties+json'

If we look at the results returned, we can see the description has changed.

PS C:\> $dcPatchRequest | fl

id         : [new-data-center-id]
type       : datacenter
href       : https://api.profitbricks.com/rest/v2/datacenters/[new-data-center-id]
metadata   : @{createdDate=[date-timestamp]; createdBy=username@domain.tld; etag=[etag];lastModifiedDate=[date-timestamp];lastModifiedBy=username@domain.tld; state=BUSY}
properties : @{name=Example DC; description=The Example DC; location=us/las; version=1; features=System.Object[]}
entities   : @{servers=; volumes=; loadbalancers=; lans=}

or specifically properties:

PS C:\> $dcPatchRequest.properties | fl

name        : Example DC
description : The Example DC
location    : us/las
version     : 1
features    : {}

An alternative to the PATCH request is a PUT request. When making a PUT request, we need to pass in ALL the values of an object. This includes the values being changed, as well as the values being left unchanged. The PUT request is built similarly to PATCH, however we specify -Method Put and -ContentType 'application/vnd.profitbricks.resource+json' as parameters when calling Invoke-RestMethod.

Make a DELETE Request

We are finished with our data center object and wish to remove it entirely. We are absolutely sure that we want to do this, so we will put together a DELETE request. We do not supply a -Body or -Content-Type, simply change the -Method to Delete and supply the appropriate object reference.

$dcDeleteRequest = Invoke-RestMethod 'https://api.profitbricks.com/rest/v2/datacenters/[new-data-center-id]' -Headers $headers -Method Delete

When accepted, the response body is empty, therefore $dcDeleteRequest will be empty.

Invoke-WebRequest

If you find that you need access to the raw response including HTTP response headers, you may consider using the Invoke-WebRequest cmdlet.

Making a GET request using Invoke-WebRequest is done like this:

PS C:> $webResponse = Invoke-WebRequest 'https://api.profitbricks.com/rest/v2/datacenters' -Headers $headers

When we examine the $webResponse object, we can see it includes some additional information, similar to what is returned when using curl with the --include option to show response headers.

PS C:\> $webResponse | fl

StatusCode        : 200
StatusDescription : OK
Content           : {123, 10, 32, 32...}
RawContent        : HTTP/1.1 200 OK
                Pragma: No-cache
                X-RateLimit-Remaining: 299
                X-RateLimit-Burst: 300
                X-RateLimit-Limit: 600
                Vary: Accept-Encoding
                Strict-Transport-Security: max-age=15768000
                Keep-Alive: timeout=5...
Headers           : {[Pragma, No-cache], [X-RateLimit-Remaining, 299], [X-RateLimit-Burst, 300], [X-RateLimit-Limit, 600]...}
RawContentLength  : 875

Now we have access to the HTTP response headers as key/value pairs:

PS C:\> $webResponse.Headers | ft

Key                       Value
---                       -----
Pragma                    No-cache
X-RateLimit-Remaining     299
X-RateLimit-Burst         300
X-RateLimit-Limit         600
Vary                      Accept-Encoding
Strict-Transport-Security max-age=15768000
Keep-Alive                timeout=5, max=100
Connection                Keep-Alive
Transfer-Encoding         chunked
Cache-Control             no-cache
Content-Type              application/vnd.profitbricks.collection+json
Date                      [date-timestamp] GMT
Expires                   Thu, 01 Jan 1970 00:00:00 UTC
ETag                      [etag]
Server                    ""

None of the headers in response to a GET request jumps out as particularly useful. However, when we make POST, PATCH, PUT, or DELETE request, we will get a Location header returned that can be used to query the status of the request. For example, if we make the same POST request used to create a new data center, but this time with Invoke-WebRequest:

PS C:\> $dcPostWebRequest = Invoke-WebRequest 'https://api.profitbricks.com/rest/v2/datacenters' -Method Post -Headers $headers -Body $CreateDc -ContentType 'application/vnd.profitbricks.resource+json'

We get the following back in $dcPostWebRequest.Headers:

PS C:\> $dcPostWebRequest.Headers | ft

Key                       Value
---                       -----
X-RateLimit-Remaining     49
X-RateLimit-Burst         50
X-RateLimit-Limit         120
Strict-Transport-Security max-age=15768000
Keep-Alive                timeout=5, max=100
Connection                Keep-Alive
Transfer-Encoding         chunked
Content-Type              application/vnd.profitbricks.resource+json
Date                       [date-timestamp] GMT
ETag                      [etag]
Location                  https://api.profitbricks.com/rest/v2/requests/[request-status-id]/status
Server                    ""

Looking at a snippet of the JSON response in $dcPostWebRequest.RawContent we can see:

"state" : "BUSY"
},
"properties" : {
  "name" : "Example DC",
  "description" : "An Example DC",
  "location" : "us/las",
  "version" : null,
  "features" : [ ]
}

If we craft a GET request against the value returned for Location, we can find out the status of the request:

PS C:\> $dcCheckStatus = Invoke-WebRequest 'https://api.profitbricks.com/rest/v2/requests/[request-status-id]/status' -Method Get -Headers $headers

And a snippet of the JSON response shows that our request to create a data center has completed:

PS C:\> $dcCheckStatus.RawContent | fl

{
  "id" : "[request-status-id]/status",
  "type" : "request-status",
  "href" : "https://api.profitbricks.com/rest/v2/requests/[request-status-id]/status",
  "metadata" : {
    "status" : "DONE",
    "message" : "Request has been successfully executed",
    "etag" : "[etag]",
    "targets" : [ {
      "target" : {
        "id" : "[new-data-center-id]",
        "type" : "datacenter",
        "href" : "https://api.profitbricks.com/rest/v2/datacenters/[new-data-center-id]"
      },
      "status" : "DONE"
    } ]
  }
}

Summary

We have briefly demonstrated how to make GET, PATCH, and DELETE requests against the ProfitBricks REST API using the Invoke-RestMethod cmdlet in Windows PowerShell. We also discussed how to use the Invoke-WebRequest cmdlet to perform similar operations and its ability to capture data returned in the response headers.