Hetzner DNS via Terraform
01. July 2020 - Hetzner just launched Free DNS Management solution.
EDIT: You can use my link https://hetzner.cloud/?ref=iMdF1UjNssSX and you will receive € 20 in cloud credits.
Here is my tutorial, how to setup your DNS records programmatically via Terraform
Install Terraform Provider for Hetzner DNS - https://github.com/timohirt/terraform-provider-hetznerdns
You can use insekticid/k8s-upgrade:latest
My Docker image with Terraform + Provider for Hetzner DNS plugin https://hub.docker.com/r/insekticid/k8s-upgrade
docker run --rm -it --entrypoint=sh -v "$(pwd):/app" insektici
d/k8s-upgrade
Terraform directory structure
/app/
├── modules/
│ └── records/
│ ├── main.tf
│ ├── variables.tf
│ └── outputs.tf (empty)
├── .env
├── .env.dist
├── docker-compose.yml
├── main.tf
└── terraform.tfvars
- mkdir modules/records
- cd modules/records
- touch main.tf outputs.tf variables.tf
modules/records/main.tf
resource "hetznerdns_zone" "zone1" {
name = var.domain
ttl = 60
}
resource "hetznerdns_record" "root" {
zone_id = hetznerdns_zone.zone1.id
name = "@"
value = var.ip_v4
type = "A"
ttl = 60
}
resource "hetznerdns_record" "root6" {
zone_id = hetznerdns_zone.zone1.id
name = "@"
value = var.ip_v6
type = "AAAA"
ttl = 60
count = var.ip_v6 != "" ? 1 : 0
}
resource "hetznerdns_record" "www" {
zone_id = hetznerdns_zone.zone1.id
name = "www"
value = "${var.domain}."
type = "CNAME"
ttl = 3600
}
resource "hetznerdns_record" "all" {
zone_id = hetznerdns_zone.zone1.id
name = "*"
value = "${var.domain}."
type = "CNAME"
ttl = 3600
}
resource "hetznerdns_record" "mx" {
zone_id = hetznerdns_zone.zone1.id
name = "@"
value = "1 aspmx.l.google.com."
type = "MX"
ttl = 3600
}
resource "hetznerdns_record" "mxalt1" {
zone_id = hetznerdns_zone.zone1.id
name = "@"
value = "5 alt1.aspmx.l.google.com."
type = "MX"
ttl = 3600
}
resource "hetznerdns_record" "mxalt2" {
zone_id = hetznerdns_zone.zone1.id
name = "@"
value = "5 alt2.aspmx.l.google.com."
type = "MX"
ttl = 3600
}
resource "hetznerdns_record" "mxalt3" {
zone_id = hetznerdns_zone.zone1.id
name = "@"
value = "10 alt3.aspmx.l.google.com."
type = "MX"
ttl = 3600
}
resource "hetznerdns_record" "mxalt4" {
zone_id = hetznerdns_zone.zone1.id
name = "@"
value = "10 alt4.aspmx.l.google.com."
type = "MX"
ttl = 3600
}
resource "hetznerdns_record" "txtgoogle" {
zone_id = hetznerdns_zone.zone1.id
name = "@"
value = var.google_site_verification
type = "TXT"
ttl = 3600
count = var.google_site_verification != "" ? 1 : 0
}
resource "hetznerdns_record" "txtdkim" {
zone_id = hetznerdns_zone.zone1.id
name = "google._domainkey"
value = var.dkim
type = "TXT"
ttl = 3600
count = var.dkim != "" ? 1 : 0
}
resource "hetznerdns_record" "txtspf" {
zone_id = hetznerdns_zone.zone1.id
name = "@"
value = var.spf
type = "TXT"
ttl = 3600
count = var.spf != "" ? 1 : 0
}
resource "hetznerdns_record" "txtdmarc" {
zone_id = hetznerdns_zone.zone1.id
name = "_dmarc"
value = replace(var.dmarc, "DOMAIN", hetznerdns_zone.zone1.name)
type = "TXT"
ttl = 3600
count = var.dmarc != "" ? 1 : 0
}
resource "hetznerdns_record" "txtbrave" {
zone_id = hetznerdns_zone.zone1.id
name = "@"
value = var.brave_site_verification
type = "TXT"
ttl = 3600
count = var.brave_site_verification != "" ? 1 : 0
}
resource "hetznerdns_record" "gsuite_mail" {
zone_id = hetznerdns_zone.zone1.id
name = "mail"
value = "ghs.google.com."
type = "CNAME"
ttl = 3600
count = var.gsuite != "" ? 1 : 0
}
resource "hetznerdns_record" "gsuite_calendar" {
zone_id = hetznerdns_zone.zone1.id
name = "calendar"
value = "ghs.google.com."
type = "CNAME"
ttl = 3600
count = var.gsuite != "" ? 1 : 0
}
resource "hetznerdns_record" "gsuite_docs" {
zone_id = hetznerdns_zone.zone1.id
name = "docs"
value = "ghs.google.com."
type = "CNAME"
ttl = 3600
count = var.gsuite != "" ? 1 : 0
}
modules/records/variables.tf
variable "domain" {}
variable "google_site_verification" {
default = ""
}
variable "brave_site_verification" {
default = ""
}
variable "bing_site_verification" {
default = ""
}
variable "dkim" {
default = ""
}
variable "spf" {
default = ""
}
variable "dmarc" {
default = ""
}
variable "gsuite" {
default = "1"
}
variable "ip_v4" {}
variable "ip_v6" {
default = ""
}
main.tf
# you can save your state in S3 bucket instead of local file
#terraform {
# backend "s3" {
# bucket = "terraform-shared-state-xyz"
# region = "eu-west-1"
# key = "dns/terraform.tfstate"
# }
#}
# you can get another data from you remote state S3 bucket
# e.g. your cluster LB ipv4/ipv6
#data "terraform_remote_state" "cluster" {
# backend = "s3"
# config {
# bucket = "terraform-shared-state-xyz"
# region = "eu-west-1"
# key = "cluster/terraform.tfstate"
# }
#}
locals {
#ip_v4 = data.terraform_remote_state.cluster.lb_ip_v4
#ip_v6 = data.terraform_remote_state.cluster.lb_ip_v6
ip_v4 = "yourIPv4"
ip_v6 = "yourIPv6"
spf = "v=spf1 include:_spf.google.com ~all"
#do not modify DOMAIN string! it will be replaced with domain name automatically
dmarc = "v=DMARC1; p=quarantine; rua=mailto:dmarc-reports@DOMAIN; pct=20; sp=none"
}
#variable "hetznerdns_token" {}
provider "hetznerdns" {
#apitoken = var.hetznerdns_token
}
module "example_com_records" {
source = "./modules/records"
domain = "example.com"
ip_v4 = local.ip_v4
ip_v6 = local.ip_v6
dkim = "v=DKIM1; k=rsa; p=YOURRSAKEY"
spf = local.spf
dmarc = local.dmarc
google_site_verification = "google-site-verification=YOURGOOGLEVERIFYKEY"
bing_site_verification = "YOURBINVERIFYKEY"
brave_site_verification = "brave-ledger-verification=YOURBRAVEVERIFYKEY"
}
# example 2
# without IPv6
# only google site verification
# without gsuite subdomains - default is ON
module "example2_com_records" {
source = "./modules/records"
domain = "example2.com"
ip_v4 = local.ip_v4
gsuite = ""
google_site_verification = "google-site-verification=YOURGOOGLEVERIFYKEY"
}
terraform.tfvars
#hetznerdns_token = yourhetznerdnsapitoken
You can use HETZNER_DNS_API_TOKEN=yourhetznerdnsapitoken
Now you can run terraform init
, terraform plan
, terraform apply -auto-approve
Now change DNS records for your domain in your domain registrator console
Edit: use docker-compose.yaml
create new file docker-compose.yaml in your terraform directory
version: '2.2'
services:
terraform:
image: insekticid/k8s-upgrade
volumes:
- ./:/terraform/
env_file: .env
working_dir: /terraform
create .env.dist
AWS_ACCESS_KEY_ID="anaccesskey"
AWS_SECRET_ACCESS_KEY="asecretkey"
AWS_DEFAULT_REGION="eu-west-1"
HETZNER_DNS_API_TOKEN="andnsapitoken"
Copy env dist to .env file and edit you secrets
cp .env.dist .env and
Now you can run
- docker-compose run --rm terraform init
- docker-compose run --rm terraform plan
- docker-compose run --rm terraform apply