How to work with multiple projects in Hetzner Cloud

Posted on April 16, 2021 by Adrian Wyssmann ‐ 3 min read

In Hetzner Cloud you can have multiple projects. As you can have different members per group, it really makes sense to have different projects for different purposes. I will explain in this article on how I work with multiple projects in Ansible, as well when using the cli

Multiple projects in Hetzner Cloud

A project in Hetzner Cloud has a dedicated set of members and resources, whereas each member has a role, which determines what they are allowed to do. In my case I have two projects, to separate productive systems from my playground/development resources:

projects in hetzner cloud

How to access projects using the cli

Access to projects is controlled by the API token, which you have to create via the Cloud Console:

api tokens in Hetzner cloud

You can set the token as environment variable HCLOUD_TOKEN which can be used for both, ansible and the hcloud cli. However as you can have only one environment variable with this name, this may cause undesired side effect, as depending on the value set, you access either one or the other project. Luckily hcloud cli has context. You can define contexts by giving it a name and the project-specific token for example:

$ hcloud context create development
Token:
Context development created and activated
$ hcloud context create production
Token:
Context production created and activated
$ hcloud context list
ACTIVE   NAME
         development
*        production

Let’s see how this works

$ hcloud context active
production
$ hcloud server list
ID        NAME    STATUS    IPV4             IPV6                     DATACENTER
9830839   ttrss   running   x.x.x.x          aa:aa:aa:aa::/64         hel1-dc2
$ hcloud context use development
$ hcloud server list
ID         NAME      STATUS    IPV4            IPV6                      DATACENTER
11309447   dev0001   running   x.x.x.x         aa:aa:aa:aa::/64          hel1-dc2

Dynamic inventory in Ansible with multiple projects

I already wrote in one of my recent posts how manage inventory with inventory plugins using the hcloud inventory plugin and hetzner robot inventory plugin. If you use Hetzner Cloud. So far I started with a single inventory file inventory.hcloud.yml which looked like this:

plugin: hcloud
keyed_groups:
  - key: location
    prefix: hcloud_location
  - key: image_os_flavor
    separator: ""
  - key: status
    prefix: server_status
  - key: labels
    separator: ""
compose:
  server_ipv4: ipv4
  server_ipv6: ipv6
  ansible_host: name ~ ".example.com"

So far I always had HCLOUD_TOKEN set but when removing this will not work anymore:

$ ansible-inventory -i inventory.hcloud.yml  --graph
[WARNING]:  * Failed to parse /home/aedu/Workspaces/example.com/infrastructure/inventory.hcloud.yml with auto plugin: Invalid Hetzner Cloud API Token.
[WARNING]:  * Failed to parse /home/aedu/Workspaces/example.com/infrastructure/inventory.hcloud.yml with yaml plugin: Plugin configuration YAML file, not YAML inventory
[WARNING]:  * Failed to parse /home/aedu/Workspaces/example.com/infrastructure/inventory.hcloud.yml with ini plugin: Invalid host pattern 'plugin:' supplied, ending in ':' is not allowed, this character is
reserved to provide a port.
[WARNING]: Unable to parse /home/aedu/Workspaces/example.com/infrastructure/inventory.hcloud.yml as an inventory source
[WARNING]: No inventory was parsed, only implicit localhost is available
@all:
  |--@ungrouped:

If you check carefully in the docu of hcloud inventory plugin you can see that one can set the token. So let`s do that - obviously using an encrypted string:

token: !vault |
          $ANSIBLE_VAULT;1.1;AES256
          xxxxxx

And see, it’s working

$ ansible-inventory -i inventory.hcloud.yml  --graph
@all:
  |--@debian:
  |  |--dev0001
  |--@dev:
  |  |--dev0001
  |--@hcloud:
  |  |--dev0001
  |--@hcloud_location_hel1:
  |  |--dev0001
  |--@k8smaster:
  |  |--dev0001
  |--@server_status_running:
  |  |--dev0001
  |--@ungrouped:

As I have configured inventory in the ansible.cfg the above also works without the -i parameter. So as I have 2 projects, I also need 2 inventory files - inventory.dev.hcloud.yml and inventory.prd.hcloud.yml - both with the same configuration, but different token. I also update ansible.cfg as follows:

inventory      = ./inventory.yml,./inventory.dev.hcloud.yml,./inventory.prd.hcloud.yml

This is sufficient so that I can use the inventory of both projects - dev0001 runs in development whereas ttrss runs in production:

$ ansible-inventory --graph
@all:
  |--@debian:
  |  |--dev0001
  |  |--ttrss
  |--@dev:
  |  |--dev0001
  |--@hcloud:
  |  |--dev0001
  |  |--ttrss
  |--@hcloud_location_hel1:
  |  |--dev0001
  |  |--ttrss
  |--@k8smaster:
  |  |--dev0001
  |--@prod:
  |  |--ttrss
  |--@rss:
  |  |--ttrss
  |--@server_status_running:
  |  |--dev0001
  |  |--ttrss
  |--@ungrouped: