commit fecf871eb11accdf2e38b419f7cd34daec766b63 Author: alessandrovitali Date: Tue Aug 18 13:01:36 2026 +0200 feat: terraform + ansible config to provision a host on hetzner & set cloudflare DNS diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..36d0546 --- /dev/null +++ b/.gitignore @@ -0,0 +1,21 @@ +# Terraform +.terraform/ +*.tfstate +*.tfstate.backup +*.tfvars +*.tfvars.json +.terraform.lock.hcl + +# Ansible +ansible/inventory.ini + +# Private Keys & Secrets +*.pem +*.key +*.pub +id_rsa +id_rsa.pub + +# OS files +.DS_Store +Thumbs.db diff --git a/ansible/ansible.cfg b/ansible/ansible.cfg new file mode 100644 index 0000000..4dd424d --- /dev/null +++ b/ansible/ansible.cfg @@ -0,0 +1,3 @@ +[defaults] +inventory = inventory.ini +host_key_checking = False diff --git a/ansible/group_vars/all.yml b/ansible/group_vars/all.yml new file mode 100644 index 0000000..b732479 --- /dev/null +++ b/ansible/group_vars/all.yml @@ -0,0 +1,5 @@ +--- +ansible_user: "ansible" + +sysadmin_user: "alessandrovitali" +sysadmin_public_ssh_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAm/J+9YG+odym9In9C4iLcrfXlrlPK2TygtI7lBNNpl" diff --git a/ansible/inventory.tpl b/ansible/inventory.tpl new file mode 100644 index 0000000..253aab1 --- /dev/null +++ b/ansible/inventory.tpl @@ -0,0 +1,2 @@ +[dokploy] +dokploy ansible_host=${public_ip} ansible_user=debian diff --git a/ansible/playbook.yml b/ansible/playbook.yml new file mode 100644 index 0000000..f6e0646 --- /dev/null +++ b/ansible/playbook.yml @@ -0,0 +1,8 @@ +## ansible/setup.yml + +- name: Setup dokploy machine + hosts: all + roles: + - role: studio.ansible.base + - role: studio.ansible.users + - role: studio.ansible.security diff --git a/ansible/requirements.yml b/ansible/requirements.yml new file mode 100644 index 0000000..f9a3c0a --- /dev/null +++ b/ansible/requirements.yml @@ -0,0 +1,7 @@ +## ansible/requirements.yml + +collections: + - name: studio.ansible + source: https://git.studiovita.li/studio/ansible.git ## HTTPS + type: git + version: dokploy diff --git a/terraform/main.tf b/terraform/main.tf new file mode 100644 index 0000000..3b5e092 --- /dev/null +++ b/terraform/main.tf @@ -0,0 +1,52 @@ +# SSH key shared with the VPS +resource "hcloud_ssh_key" "ansible" { + name = var.project_name + public_key = var.ssh_public_key +} + +# Provision a small Hetzner Cloud VPS +resource "hcloud_server" "dokploy" { + name = var.project_name + image = var.hcloud_image + server_type = var.hcloud_server_type + location = var.hcloud_location + ssh_keys = [hcloud_ssh_key.ansible.id] + + public_net { + ipv4_enabled = true + } +} + +# Resolve the Cloudflare zone from its apex domain name +data "cloudflare_zone" "zone" { + filter = { + name = var.domain_name + } +} + +locals { + public_ip = hcloud_server.dokploy.ipv4_address +} + +# Automatically publish the VPS IP as a Cloudflare A record +resource "cloudflare_dns_record" "a" { + zone_id = data.cloudflare_zone.zone.id + name = var.record_name + type = "A" + content = local.public_ip + proxied = var.cloudflare_proxied + ttl = var.cloudflare_proxied ? 1 : 3600 +} + +# Write IP to Ansible inventory +resource "local_file" "ansible_inventory" { + content = templatefile("${path.module}/../ansible/inventory.tpl", { + public_ip = local.public_ip + }) + filename = "${path.module}/../ansible/inventory.ini" +} + +output "public_ip" { + description = "The public IPv4 address of the provisioned Hetzner Cloud VPS" + value = local.public_ip +} diff --git a/terraform/providers.tf b/terraform/providers.tf new file mode 100644 index 0000000..0ed312a --- /dev/null +++ b/terraform/providers.tf @@ -0,0 +1,25 @@ +terraform { + required_version = ">= 1.0.0" + required_providers { + hcloud = { + source = "hetznercloud/hcloud" + version = "~> 1.68.0" + } + cloudflare = { + source = "cloudflare/cloudflare" + version = "~> 5.23.0" + } + local = { + source = "hashicorp/local" + version = "~> 2.9.0" + } + } +} + +provider "hcloud" { + token = var.hcloud_token +} + +provider "cloudflare" { + api_token = var.cloudflare_api_token +} diff --git a/terraform/variables.tf b/terraform/variables.tf new file mode 100644 index 0000000..1cf5fa0 --- /dev/null +++ b/terraform/variables.tf @@ -0,0 +1,58 @@ +variable "project_name" { + type = string + description = "VM name" +} + +variable "ssh_public_key" { + type = string + description = "Public SSH key to install on the VM" +} + +## HETZNER +variable "hcloud_token" { + type = string + description = "Hetzner Cloud API token" + sensitive = true +} + +variable "hcloud_server_type" { + type = string + description = "Hetzner Cloud server type" + default = "cx22" # 2 vCPU / 4 GB RAM / 40 GB NVMe +} + +variable "hcloud_image" { + type = string + description = "OS image name to boot the VPS from" + default = "debian-13" +} + +variable "hcloud_location" { + type = string + description = "Hetzner Cloud location" + default = "nbg1" # Nuremberg, Germany +} + +## CLOUDFLARE +variable "cloudflare_api_token" { + type = string + description = "Cloudflare API token with Zone.DNS edit permission" + sensitive = true +} + +variable "domain_name" { + type = string + description = "Cloudflare zone (apex domain) in which to create the A record" +} + +variable "record_name" { + type = string + description = "DNS record name relative to the zone; use \"@\" for the apex" + default = "dokploy" +} + +variable "cloudflare_proxied" { + type = bool + description = "Whether Cloudflare should proxy (orange-cloud) the A record" + default = true +}