diff --git a/terraform/modules/hetzner_vm/main.tf b/terraform/modules/hetzner_vm/main.tf new file mode 100644 index 0000000..038e236 --- /dev/null +++ b/terraform/modules/hetzner_vm/main.tf @@ -0,0 +1,51 @@ +# cloud-init: create the unprivileged `ansible` user with ubongo's key + sudo. +# (Mirrors the proxmox_vm module's user_account; Hetzner has no structured field.) +locals { + user_data = <<-EOT + #cloud-config + users: + - name: ansible + groups: [sudo] + sudo: "ALL=(ALL) NOPASSWD:ALL" + shell: /bin/bash + ssh_authorized_keys: + - ${var.ansible_ssh_pubkey} + package_update: true + packages: + - python3 + EOT +} + +resource "hcloud_ssh_key" "ansible" { + name = "${var.name}-ansible" + public_key = var.ansible_ssh_pubkey +} + +resource "hcloud_firewall" "this" { + name = "${var.name}-fw" + + # SSH from the control node only. NetBird ports (UDP 3478, TCP 80/443) are added + # in M4 when the coordinator deploys (ADR-020); host nftables stays catalog-driven. + rule { + direction = "in" + protocol = "tcp" + port = "22" + source_ips = var.ssh_admin_cidrs + } +} + +resource "hcloud_server" "this" { + name = var.name + server_type = var.server_type + location = var.location + image = var.image + ssh_keys = [hcloud_ssh_key.ansible.id] + user_data = local.user_data + firewall_ids = [hcloud_firewall.this.id] + labels = var.labels + + public_net { + ipv4_enabled = true + ipv6_enabled = true + } +} diff --git a/terraform/modules/hetzner_vm/outputs.tf b/terraform/modules/hetzner_vm/outputs.tf new file mode 100644 index 0000000..90ef226 --- /dev/null +++ b/terraform/modules/hetzner_vm/outputs.tf @@ -0,0 +1,9 @@ +output "ipv4_address" { + description = "Server public IPv4" + value = hcloud_server.this.ipv4_address +} + +output "name" { + description = "Server name" + value = hcloud_server.this.name +} diff --git a/terraform/modules/hetzner_vm/variables.tf b/terraform/modules/hetzner_vm/variables.tf new file mode 100644 index 0000000..0cc73b8 --- /dev/null +++ b/terraform/modules/hetzner_vm/variables.tf @@ -0,0 +1,35 @@ +variable "name" { + description = "Server name (and hostname)" + type = string +} + +variable "server_type" { + description = "Hetzner server type, e.g. cax11 (ARM)" + type = string +} + +variable "location" { + description = "Hetzner location, e.g. hel1" + type = string +} + +variable "image" { + description = "OS image slug, e.g. debian-13" + type = string +} + +variable "ansible_ssh_pubkey" { + description = "Public SSH key provisioned for the ansible user via cloud-init" + type = string +} + +variable "ssh_admin_cidrs" { + description = "Source CIDRs allowed to reach SSH (e.g. ubongo's address/32)" + type = list(string) +} + +variable "labels" { + description = "Hetzner resource labels (metadata only)" + type = map(string) + default = {} +}