In this blog post, we will be testing the Hetzner Cloud API. As a “Hello World”, we will learn how to add an API key to a project and use the API key to

  • add an ssh key
  • create, read and delete a server.

On a previous blog post, we have explored the new Hetzner Cloud Service. While not being comparable with AWS in terms of features and completeness of the offer, we have seen that you can rent virtual machines at very competitive prices. Hetzner Cloud offers a REST API that we will use to automate the infrastructure.

In this blog post, we will use simple cURL commands to explore the API.

Hetzner Cloud API: The official Documentation

The documentation of the API can be found here (click the screenshot image below):

Now let us get started…

Step 0: Register with Hetzner Cloud

After you have registered with Hetzner Cloud, there are two more steps that we need need to be performed manually on the graphical user interface, before we can start with the infrastructure automation:

  1. creating a project
  2. creating an API token.

Let us start:

Step 1: Create Test Project

Login to Hetzner Cloud and create a new Project.

I have called my project “API Test Project”:

Step 2: Create Hetzner Cloud API Token

To create an API Token, enter the project:

In the left pane, choose access:

and then choose API TOKENS:

–> Generate API Token

Copy and save the token to a secure place.

Step 3: Find the API Documentation

From there, we can find the API Documentation:

Step 4: Specify an SSH Key

From the API Documentation, let us copy and paste the “Create SSH Key” example:

API_TOKEN=yourtoken
SSH_KEY="ssh-rsa AAAjjk76kgf...Xt"
curl -X POST \
     -H "Content-Type: application/json" \
     -H "Authorization: Bearer $API_TOKEN" \
     -d '{"name":"My ssh key","public_key":"'"$SSH_KEY"'"}' \
    https://api.hetzner.cloud/v1/ssh_keys

We have improved the example slightly by prepending the definition of the API_TOKEN and SSH_KEY. We also had to split the POST body (-d option) into three parts, since the SSH_KEY needs to be evaluated.

If everything went Okay, then the answer will look similar to follows:

{
  "ssh_key": {
    "id": 19400,
    "name": "My ssh key",
    "fingerprint": "0e:...d",
    "public_key": "ssh-rsa AAAjjk76kgf...Xt"
  }
}

Step 5: Read, Create and Delete a Server

Step 5.1: Read List of Servers

Let us create a server now. First, let us try out the “get all servers example” from the documentation:

curl -H "Authorization: Bearer $API_TOKEN" \
   https://api.hetzner.cloud/v1/servers

This should return an empty list since the project is new and we have not created any servers yet:

{
  "servers": [],
  "meta": {
    "pagination": {
      "page": 1,
      "per_page": 25,
      "previous_page": null,
      "next_page": null,
      "last_page": 1,
      "total_entries": 0
    }
  }
}

We already can see above that Hetzner’s cloud API works supports pagination. I.e., if the number of returned entries is larger than 25, we need to perform more than one request to get the full list.

Step 5.2: Create a Server

Now let us create a server as follows:

curl -X POST \
     -H "Content-Type: application/json" \
     -H "Authorization: Bearer $API_TOKEN"  \
     -d '{"name":"my-server","server_type":"cx11","start_after_create":true,"image":"ubuntu-16.04","ssh_keys":["My ssh key"],"user_data":"#cloud-config\nruncmd:\n- [touch,/root/cloud-init-worked]\n"}' \
     https://api.hetzner.cloud/v1/servers

This time the answer is kind of lengthy:

{
  "server": {
    "id": 576675,
    "name": "my-server",
    "status": "initializing",
    "created": "2018-03-20T22:08:24+00:00",
    "public_net": {
      "ipv4": {
        "ip": "195.201.98.27",
        "blocked": false,
        "dns_ptr": "static.27.98.201.195.clients.your-server.de"
      },
      "ipv6": {
        "ip": "2a01:4f8:1c0c:7211::/64",
        "blocked": false,
        "dns_ptr": []
      },
      "floating_ips": []
    },
    "server_type": {
      "id": 1,
      "name": "cx11",
      "description": "CX11",
      "cores": 1,
      "memory": 2.0,
      "disk": 20,
      "prices": [
        {
          "location": "fsn1",
          "price_hourly": {
            "net": "0.0040000000",
            "gross": "0.0047600000000000"
          },
          "price_monthly": {
            "net": "2.4900000000",
            "gross": "2.9631000000000000"
          }
        },
        {
          "location": "nbg1",
          "price_hourly": {
            "net": "0.0040000000",
            "gross": "0.0047600000000000"
          },
          "price_monthly": {
            "net": "2.4900000000",
            "gross": "2.9631000000000000"
          }
        }
      ],
      "storage_type": "local"
    },
    "datacenter": {
      "id": 2,
      "name": "nbg1-dc3",
      "description": "Nuremberg 1 DC 3",
      "location": {
        "id": 2,
        "name": "nbg1",
        "description": "Nuremberg DC Park 1",
        "country": "DE",
        "city": "Nuremberg",
        "latitude": 49.452102,
        "longitude": 11.076665
      },
      "server_types": {
        "supported": [
          1,
          2,
          3,
          4,
          5,
          6,
          7,
          8,
          9,
          10
        ],
        "available": [
          1,
          2,
          3,
          4,
          5,
          6,
          7,
          8,
          9,
          10
        ]
      }
    },
    "image": {
      "id": 1,
      "type": "system",
      "status": "available",
      "name": "ubuntu-16.04",
      "description": "Ubuntu 16.04",
      "image_size": null,
      "disk_size": 5,
      "created": "2018-01-15T11:34:45+00:00",
      "created_from": null,
      "bound_to": null,
      "os_flavor": "ubuntu",
      "os_version": "16.04",
      "rapid_deploy": true
    },
    "iso": null,
    "rescue_enabled": false,
    "locked": false,
    "backup_window": null,
    "outgoing_traffic": 0,
    "ingoing_traffic": 0,
    "included_traffic": 21990232555520
  },
  "action": {
    "id": 499791,
    "command": "create_server",
    "status": "running",
    "progress": 0,
    "started": "2018-03-20T22:08:25+00:00",
    "finished": null,
    "resources": [
      {
        "id": 576675,
        "type": "server"
      }
    ],
    "error": null
  },
  "root_password": null
}

As expected, you will be able to see the server on Cloud Console:

Now let us repeat the “Get all Servers” example.

curl -H "Authorization: Bearer $API_TOKEN" \
   https://api.hetzner.cloud/v1/servers

This time, we expect to see our server on the list:

{
  "servers": [
    {
      "id": 576675,
      "name": "my-server",
      "status": "running",
      "created": "2018-03-20T22:08:24+00:00",
      "public_net": {
        "ipv4": {
          "ip": "195.201.98.27",
          "blocked": false,
          "dns_ptr": "static.27.98.201.195.clients.your-server.de"
        },
        "ipv6": {
          "ip": "2a01:4f8:1c0c:7211::/64",
          "blocked": false,
          "dns_ptr": []
        },
        "floating_ips": []
      },
      "server_type": {
        "id": 1,
        "name": "cx11",
        "description": "CX11",
        "cores": 1,
        "memory": 2.0,
        "disk": 20,
        "prices": [
          {
            "location": "fsn1",
            "price_hourly": {
              "net": "0.0040000000",
              "gross": "0.0047600000000000"
            },
            "price_monthly": {
              "net": "2.4900000000",
              "gross": "2.9631000000000000"
            }
          },
          {
            "location": "nbg1",
            "price_hourly": {
              "net": "0.0040000000",
              "gross": "0.0047600000000000"
            },
            "price_monthly": {
              "net": "2.4900000000",
              "gross": "2.9631000000000000"
            }
          }
        ],
        "storage_type": "local"
      },
      "datacenter": {
        "id": 2,
        "name": "nbg1-dc3",
        "description": "Nuremberg 1 DC 3",
        "location": {
          "id": 2,
          "name": "nbg1",
          "description": "Nuremberg DC Park 1",
          "country": "DE",
          "city": "Nuremberg",
          "latitude": 49.452102,
          "longitude": 11.076665
        },
        "server_types": {
          "supported": [
            1,
            2,
            3,
            4,
            5,
            6,
            7,
            8,
            9,
            10
          ],
          "available": [
            1,
            2,
            3,
            4,
            5,
            6,
            7,
            8,
            9,
            10
          ]
        }
      },
      "image": {
        "id": 1,
        "type": "system",
        "status": "available",
        "name": "ubuntu-16.04",
        "description": "Ubuntu 16.04",
        "image_size": null,
        "disk_size": 5,
        "created": "2018-01-15T11:34:45+00:00",
        "created_from": null,
        "bound_to": null,
        "os_flavor": "ubuntu",
        "os_version": "16.04",
        "rapid_deploy": true
      },
      "iso": null,
      "rescue_enabled": false,
      "locked": false,
      "backup_window": null,
      "outgoing_traffic": 0,
      "ingoing_traffic": 0,
      "included_traffic": 21990232555520
    }
  ],
  "meta": {
    "pagination": {
      "page": 1,
      "per_page": 25,
      "previous_page": null,
      "next_page": null,
      "last_page": 1,
      "total_entries": 1
    }
  }
}

Yes, we see a list of servers with length 1. The single server we see on the list is the one we have created before.

Step 5.3: Delete the Server

Now let us delete the server we just had created:

curl -X DELETE \
    -H "Content-Type: application/json" \
    -H "Authorization: Bearer $API_TOKEN" \
    https://api.hetzner.cloud/v1/servers/576675

Here, we have used the id 576675 of the server which we have seen in the response bodies of the creation or list commands, respectively.

If everything worked out well above, we will get a response with “status”:”success” as follows:

{
  "action": {
    "id": 500177,
    "command": "delete_server",
    "status": "success",
    "progress": 100,
    "started": "2018-03-20T22:22:14+00:00",
    "finished": "2018-03-20T22:22:14+00:00",
    "resources": [
      {
        "id": 576675,
        "type": "server"
      }
    ],
    "error": null
  }
}

The project will have no servers anymore, accordingly:

This should be it for this little “Hello World” example.

Summary

In this little “Hello World” example, we have seen that Hetzner’s Cloud API offers a simple token mechanism. You need to create the project and the token manually via the graphical cloud console. After that, HTTPS requests can be used to manage the project, We have shown how to add an SSH Key, and how to create, read and delete a server.

Leave a Reply