Disclaimer: This guide was written in 2019 and is outdated. Terraform, Hetzner Cloud, Cloudflare DNS, CentOS, and provider configuration have changed since publication. Please treat this post as historical reference and verify all commands against the current official documentation before using them.

Since the last post I came up with the need of automation and easy reinstallation. The first step to “total automation” is to provide vms, that then can be used by ansible. I started looking around and found Terraform, an “Infrastructure as Code” (IaC) Platform, that makes it easy to deploy and manage VMs on cloud providers like AWS, Hetzner or even your own Docker/VMWare hosts.

Since I am already using Hetzner and was not able to find a lot of info about the Hetzner cloud provider, I thought I will create a short post on my blog.

What we will need in this guide:

  • Machine or VM running CentOS7/8
  • Terraform
  • Hetzner API Key
  • Cloudflare API Key
  • Go

Installation

Terraform Installation

I started with a new CentOS 8 machine, which will be the central point of management for ansible and terraform. Then I started by installing Terraform on the machine.

Before we can download and install Terraform, make sure to update your system and install epel-release, unzip and wget. For the plugins, go is needed.

sudo yum update -y
sudo yum install wget unzip -y
sudo yum module -y install go-toolset

After the installation we can start by getting the newest release of Terraform. At the time of writing this is 0.12.12. We will download the linux package with wget.

wget https://releases.hashicorp.com/terraform/0.12.12/terraform_0.12.12_linux_amd64.zip

Next step will be to extract the file contained in the ZIP to /usr/local/bin.

sudo unzip ./terraform_0.12.12_linux_amd64.zip -d /usr/local/bin/

That’s already it. Terraform should be installed. You can check the version of Terraform by entering the following command:

terraform -v

Cloudflare & Hetzner Plugin Installation

Terraform has multiple plugins to provide features for different hosters. In this guide we will use only 2 of many, however the installation is almost always the same. The installation steps can also be found on the respective github pages. (hcloud provider, cloudflare provider) I will not comment on these steps since they are already documented on github.

Hetzner Cloud Provider
mkdir -p $GOPATH/src/github.com/terraform-providers; cd $GOPATH/src/github.com/terraform-providers
git clone https://github.com/terraform-providers/terraform-provider-hcloud.git
cd $GOPATH/src/github.com/terraform-providers/terraform-provider-hcloud
make build
Cloudflare Provider
mkdir -p $GOPATH/src/github.com/terraform-providers; cd $GOPATH/src/github.com/terraform-providers
git clone https://github.com/terraform-providers/terraform-provider-cloudflare.git
cd $GOPATH/src/github.com/terraform-providers/terraform-provider-cloudflare
make build

Configuring Terraform

Simple configuration example for Terraform

Now that we have installed Terraform we will proceed by configuring it. For this create a new directory, that will contain all you config-files in the future. Let us also create a new terraform file. Terraform files for deployment should always end in *.tf.

For configuration we need two files, one called “terraform.tf”, the other called “terraform.tfvars”. The first one will define what instances to spawn the second is for static variables like the api key of hetzner.

sudo mkdir /etc/terraform
sudo touch /etc/terraform/terraform.tfvars
sudo touch /etc/terraform/terraform.tf

Let’s start with a simple example:

hcloud_token = "YOUR_HETZNER_API_TOKEN"
/etc/terraform/terraform.tfvars
#This loads a variable for the API token of hetzner, it is defined in the terraform.tfvars file
variable "hcloud_token" {}

#Create new centos 8 node called "testnode" as cx11 instance
resource "hcloud_server" "testnode" {
name = "testnode"
image = "centos-8"
server_type = "cx11"
}

#Output the IPv4 of "testnode"
output "ipv4" {
value = "${hcloud_server.testnode.ipv4_address}"
}
/etc/terraform/terraform.tf

The above example will create a new hetzner node with the name “testnode”. Also it will output the set IPv4 of the newly created VPS.

Advanced configuration example for Terraform

Until now this was pretty standard. Let us go a few steps further, we want to not only create a new instance, we want to also add it to our cloudflare DNS provider. Adding to it, we also want our machine to be created in a specific datacenter at hetzner.

At the time of writing, Hetzner has 3 Datacenter Locations:

  • Helsinki
  • Nürnburg
  • Falkenstein

We will be creating the new machine in Nürnburg, however the code below is ready to be used for other datacenters by simply changing the value of “location”.

hcloud_token = "YOUR_HETZNER_API_TOKEN"
cloudflare_email = "YOUR_CLOUDFLARE_EMAIL"
cloudflare_api_key = "YOUR_GLOBAL_CLOUDFLARE_API_KEY"
/etc/terraform/terraform.tfvars
variable "hcloud_token" {}
variable "cloudflare_email" {}
variable "cloudflare_api_key" {}
variable "cloudflare_api_token" {}

provider "hcloud" {
token = "${var.hcloud_token}"
}

provider "cloudflare" {
api_key = "${var.cloudflare_api_key}"
#api_token = "${var.cloudflare_api_token}"
email = "${var.cloudflare_email}"
}

#Locations of Datacenters
data "hcloud_location" "fsn"{
name = "fsn1"
}
data "hcloud_location" "nbg"{
name = "nbg1"
}
data "hcloud_location" "hel"{
name = "hel1"
}

#Deploy vm with the following settings
resource "hcloud_server" "testnode" {
name = "testnode"
image = "centos-8"
server_type = "cx11"
location = "${data.hcloud_location.nbg.name}"
}
#Cloudflare set IPv4
resource "cloudflare_record" "testnode_ipv4" {
zone_id = "YOUR_ZONE_ID"
name = "${hcloud_server.testnode.name}"
value = "${hcloud_server.testnode.ipv4_address}"
type = "A"
proxied = "true"
}
#Cloudflare set IPv6
resource "cloudflare_record" "testnode_ipv6" {
zone_id = "YOUR_ZONE_ID"
name = "${hcloud_server.testnode.name}"
value = "${hcloud_server.testnode.ipv6_address}"
type = "AAAA"
proxied = "true"
}
#Output information
output "testnodeipv4" {
value = "${hcloud_server.testnode.ipv4_address}"
}
output "testnodedc" {
value = "${hcloud_server.testnode.datacenter}"
}
/etc/terraform/terraform.tf

 

Conclusion

After fixing the provider configuration and running terraform init, terraform plan and terraform apply, this setup provides a simple base for deploying a VM in a selected Hetzner datacenter and creating the matching Cloudflare DNS records.

Terraform makes it easy to keep infrastructure configuration readable and reusable. Instead of manually creating servers and DNS entries through different web interfaces, everything can be defined in code and applied whenever needed. This also makes rebuilding or extending the environment much faster.

In future steps, this setup can be expanded with SSH keys, multiple servers, firewall rules, volumes and automatic Ansible provisioning. From here, the deployed VM can serve as the starting point for further automated configuration and management.

Sources:

https://www.terraform.io/docs/providers/hcloud/index.html

https://www.terraform.io/docs/configuration/data-sources.html

https://api.cloudflare.com/