feat: add Docker/OpenTofu deployment and DE/NL routing
This commit is contained in:
@@ -0,0 +1,18 @@
|
||||
.terraform/
|
||||
.terraform.tfstate.lock.info
|
||||
*.tfstate
|
||||
*.tfstate.*
|
||||
*.tfplan
|
||||
crash.log
|
||||
crash.*.log
|
||||
|
||||
# Lokale Konfiguration mit Zugangsdaten
|
||||
terraform.tfvars
|
||||
*.auto.tfvars
|
||||
!terraform.tfvars.example
|
||||
|
||||
# Lokale Overrides
|
||||
override.tf
|
||||
override.tf.json
|
||||
*_override.tf
|
||||
*_override.tf.json
|
||||
Generated
+35
@@ -0,0 +1,35 @@
|
||||
# This file is maintained automatically by "tofu init".
|
||||
# Manual edits may be lost in future updates.
|
||||
|
||||
provider "registry.opentofu.org/hetznercloud/hcloud" {
|
||||
version = "1.66.1"
|
||||
constraints = "1.66.1"
|
||||
hashes = [
|
||||
"h1:2xnL6r7iH/7sheeBlcSC8KpT9kUSYiboo0Ujc7B0RLw=",
|
||||
"h1:4umCzzfMlXTswvnn46dX0tPNl267O72eIXRkO/+Nfng=",
|
||||
"h1:7CSl5SdPjP2VK96O/4rz4zem2WSljipRr2r2DQWDbRw=",
|
||||
"h1:JO2NGqSohoL+EfmcuUC6LdvoZh8Qd4fl0+Fqfyz+3Oc=",
|
||||
"h1:QHgbvrPciaSf3B9q4tdfPq4yK5sJpA0vq7Z5Mjfj/sg=",
|
||||
"h1:R+S77AiRWQQnVROR+HNqMXnhX8fDxZVBG1PwlpxGyss=",
|
||||
"h1:WU+1RcBhDQNRY5CcurLApBjj8o2SOXKhJCjObIX4Ic0=",
|
||||
"h1:WztH73MMX1CpUuglqMmnugXLS20nMHsntteg1hcM2xQ=",
|
||||
"h1:Z/ipcn9gDF25Sjbp0v80Alt/6v4XwiBBmd9V6mYo0I0=",
|
||||
"h1:bKv1qR7TB/x6a1u1OmacaGgFXmkAukokAPizicGvHAo=",
|
||||
"h1:cc0E0i8geVFX5z8Kt5qrRew3eyB1OaWxTBzBbkoU7tA=",
|
||||
"h1:lTWPM6oRkJ7988uDQ2bR8SUeezk7ux/rwg3WIuN2oRU=",
|
||||
"h1:rxfnUOt0pPl2XXqIzMaEJimpWjBquLOCHv5Z/4/AD6g=",
|
||||
"zh:113070176eb4fb26a3758b3d1031bb904e34a74f7c4f99f90df2301ca4468a51",
|
||||
"zh:1bfc988bdcd7c9422e09c262c736ad265a205684f0402fa4a83e63c0b08e09ea",
|
||||
"zh:37d92b4cf0f344295b0d780aabbc1408f02db31141cd4408276455a458071e54",
|
||||
"zh:386bd1207b3ed284b513294ddad9b9f59047a693c3aa375605f858f9d9758b58",
|
||||
"zh:43d26f0a4f5a64bf0ade1c5a278b40153d4ae8c77508933b9c8bb7f7860dae6d",
|
||||
"zh:6a1fe681a1706be87f0d03b749f1dc69c838d663c6ab08a00ae8d87be2e4425e",
|
||||
"zh:7032556ae2a74b2e1b7d68add9b96158e4f7202ebb8fbd75e0e8a1581719df7b",
|
||||
"zh:893296927373b4afa7ec3639abb1ebab3c1b6cb9cfe427877cfa7bf69a33480d",
|
||||
"zh:8d58b340e21428a59cc27dc4f5a7fcd2035ee1aa9372c3932f7962647532fe0d",
|
||||
"zh:93509170347bf097ca38e288a6e921811bb769bb2dfac3c339eb2032ae8454c1",
|
||||
"zh:9c00443cb5ae2401089a62e223d7d741eedb04f6a73c357b6a7443b6ae71b0be",
|
||||
"zh:9c356be5ce8cb7b1d83d5cf2276823242f106dbd0c43fe992055f0fa11290f95",
|
||||
"zh:f619116583c7e47751e0baefffca216c83f9f42edf99325121b85ee075db78cc",
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
# Watermaps auf Hetzner Cloud
|
||||
|
||||
Diese OpenTofu-Konfiguration erstellt einen einzelnen Ubuntu-24.04-Server mit
|
||||
fester IPv4-Adresse, vorgeschalteter Hetzner-Firewall und einem persistenten
|
||||
ext4-Volume für die Routingdaten von Deutschland und den Niederlanden.
|
||||
Kartenkacheln, DNS-Einträge und Anwendungsdeployment sind bewusst nicht Teil
|
||||
dieses Infrastrukturmoduls.
|
||||
|
||||
## Konfiguration
|
||||
|
||||
Die lokale Datei `terraform.tfvars` ist bereits angelegt und wird durch die
|
||||
lokale `.gitignore` ausgeschlossen. Vor dem ersten Plan müssen dort mindestens
|
||||
folgende Werte ersetzt werden:
|
||||
|
||||
- `hcloud_token`: Read/Write-API-Token des richtigen Hetzner-Cloud-Projekts
|
||||
- `ssh_public_key`: vollständiger Inhalt des öffentlichen SSH-Schlüssels
|
||||
- `admin_cidrs`: öffentliche Administrator-IP mit `/32` beziehungsweise ein
|
||||
bewusst gewähltes Netz
|
||||
|
||||
`terraform.tfvars.example` bleibt als geheimnisfreie Vorlage versioniert.
|
||||
Da `terraform.tfvars` den Token im Klartext enthält, sollte sie nur für den
|
||||
lokalen Benutzer lesbar sein:
|
||||
|
||||
```sh
|
||||
chmod 600 terraform.tfvars
|
||||
```
|
||||
|
||||
Auch `terraform.tfstate` bleibt lokal und wird nicht nach Git oder auf den
|
||||
Anwendungsserver übertragen. Nach dem ersten Apply sollte die State-Datei
|
||||
verschlüsselt gesichert werden, weil OpenTofu sie für spätere Änderungen an
|
||||
denselben Ressourcen benötigt.
|
||||
|
||||
## Infrastruktur erzeugen
|
||||
|
||||
```sh
|
||||
cd infra/opentofu
|
||||
tofu init
|
||||
tofu fmt -check
|
||||
tofu validate
|
||||
tofu plan -out=watermaps.tfplan
|
||||
tofu apply watermaps.tfplan
|
||||
```
|
||||
|
||||
Die feste IPv4-Adresse und den erforderlichen manuellen DNS-Eintrag zeigt
|
||||
OpenTofu anschließend an:
|
||||
|
||||
```sh
|
||||
tofu output server_ipv4
|
||||
tofu output dns_a_record
|
||||
tofu output ssh_command
|
||||
```
|
||||
|
||||
Der A-Record `watermaps.incoso.eu` kann direkt nach dem Apply auf
|
||||
`server_ipv4` gesetzt werden. Das eigentliche SSL-Livegehen erfolgt erst durch
|
||||
das separate Deployment-Skript, nachdem DNS propagiert ist und die Routingdaten
|
||||
bereitstehen.
|
||||
|
||||
## Auf dem Server
|
||||
|
||||
Cloud-init installiert Docker einschließlich Compose v2, `curl`, `jq` und
|
||||
`rsync`. Der Benutzer `deploy` erhält Zugriff per SSH und auf Docker. Das
|
||||
persistente Volume wird nach dem Anhängen unter `/srv/watermaps-data` gemountet
|
||||
und enthält die vom Produktions-Stack verwendeten Verzeichnisse `geofabrik`,
|
||||
`local` und `certbot`. Ein systemd-Drop-in lässt Docker bei jedem Start auf den
|
||||
erfolgreichen Volume-Mount warten, bevor bestehende Container neu gestartet
|
||||
werden.
|
||||
|
||||
Der Status des ersten Starts lässt sich so prüfen:
|
||||
|
||||
```sh
|
||||
ssh deploy@"$(tofu output -raw server_ipv4)" \
|
||||
"cloud-init status --wait && systemctl status watermaps-volume-setup --no-pager"
|
||||
```
|
||||
@@ -0,0 +1,138 @@
|
||||
#cloud-config
|
||||
|
||||
package_update: true
|
||||
package_upgrade: false
|
||||
|
||||
groups:
|
||||
- docker
|
||||
|
||||
users:
|
||||
- default
|
||||
- name: ${deploy_user}
|
||||
gecos: Watermaps deployment user
|
||||
shell: /bin/bash
|
||||
lock_passwd: true
|
||||
sudo: "ALL=(ALL) NOPASSWD:ALL"
|
||||
groups:
|
||||
- docker
|
||||
- sudo
|
||||
ssh_authorized_keys:
|
||||
- ${ssh_public_key}
|
||||
|
||||
ssh_pwauth: false
|
||||
|
||||
packages:
|
||||
- ca-certificates
|
||||
- curl
|
||||
- docker.io
|
||||
- docker-compose-v2
|
||||
- jq
|
||||
- rsync
|
||||
|
||||
write_files:
|
||||
- path: /etc/docker/daemon.json
|
||||
owner: root:root
|
||||
permissions: "0644"
|
||||
content: |
|
||||
{
|
||||
"log-driver": "json-file",
|
||||
"log-opts": {
|
||||
"max-size": "10m",
|
||||
"max-file": "3"
|
||||
}
|
||||
}
|
||||
|
||||
- path: /usr/local/sbin/watermaps-prepare-volume
|
||||
owner: root:root
|
||||
permissions: "0755"
|
||||
content: |
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
mount_path="/srv/watermaps-data"
|
||||
install -d -m 0755 "$mount_path"
|
||||
|
||||
if ! mountpoint -q "$mount_path"; then
|
||||
device=""
|
||||
for attempt in $(seq 1 120); do
|
||||
device="$(lsblk -dnpo NAME,MODEL | awk '$2 == "Volume" { print $1; exit }')"
|
||||
if [ -n "$device" ] && [ -b "$device" ]; then
|
||||
break
|
||||
fi
|
||||
device=""
|
||||
sleep 5
|
||||
done
|
||||
|
||||
if [ -z "$device" ]; then
|
||||
echo "Kein angehängtes Hetzner Cloud Volume gefunden." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
filesystem="$(blkid -o value -s TYPE "$device" || true)"
|
||||
if [ -z "$filesystem" ]; then
|
||||
mkfs.ext4 -F "$device"
|
||||
filesystem="ext4"
|
||||
fi
|
||||
|
||||
if [ "$filesystem" != "ext4" ]; then
|
||||
echo "Unerwartetes Dateisystem auf $device: $filesystem" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
uuid="$(blkid -o value -s UUID "$device")"
|
||||
if ! grep -q "^UUID=$uuid " /etc/fstab; then
|
||||
printf '%s\n' \
|
||||
"UUID=$uuid $mount_path ext4 defaults,nofail,x-systemd.device-timeout=30 0 2" \
|
||||
>> /etc/fstab
|
||||
fi
|
||||
|
||||
mount "$mount_path"
|
||||
fi
|
||||
|
||||
install -d -m 0755 -o ${deploy_user} -g ${deploy_user} \
|
||||
"$mount_path/geofabrik" \
|
||||
"$mount_path/local" \
|
||||
"$mount_path/certbot" \
|
||||
"$mount_path/certbot/www" \
|
||||
"$mount_path/certbot/letsencrypt"
|
||||
chown ${deploy_user}:${deploy_user} "$mount_path"
|
||||
|
||||
- path: /etc/systemd/system/watermaps-volume-setup.service
|
||||
owner: root:root
|
||||
permissions: "0644"
|
||||
content: |
|
||||
[Unit]
|
||||
Description=Prepare persistent Watermaps routing data volume
|
||||
Wants=network-online.target
|
||||
After=network-online.target local-fs.target
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
ExecStart=/usr/local/sbin/watermaps-prepare-volume
|
||||
RemainAfterExit=yes
|
||||
Restart=on-failure
|
||||
RestartSec=15
|
||||
TimeoutStartSec=0
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
|
||||
- path: /etc/systemd/system/docker.service.d/watermaps-volume.conf
|
||||
owner: root:root
|
||||
permissions: "0644"
|
||||
content: |
|
||||
[Unit]
|
||||
Requires=watermaps-volume-setup.service
|
||||
After=watermaps-volume-setup.service
|
||||
|
||||
runcmd:
|
||||
- [install, -d, -m, "0755", -o, ${deploy_user}, -g, ${deploy_user}, /opt/watermaps]
|
||||
- [install, -d, -m, "0755", -o, ${deploy_user}, -g, ${deploy_user}, /opt/watermaps/releases]
|
||||
- [install, -d, -m, "0755", -o, ${deploy_user}, -g, ${deploy_user}, /opt/watermaps/shared]
|
||||
- [install, -d, -m, "0755", -o, ${deploy_user}, -g, ${deploy_user}, /var/www/certbot]
|
||||
- [systemctl, daemon-reload]
|
||||
- [systemctl, enable, --now, watermaps-volume-setup.service]
|
||||
- [systemctl, enable, docker.service]
|
||||
- [systemctl, restart, docker.service]
|
||||
|
||||
final_message: "Watermaps host bootstrap completed after $UPTIME seconds."
|
||||
@@ -0,0 +1,108 @@
|
||||
locals {
|
||||
common_labels = {
|
||||
application = "watermaps"
|
||||
managed_by = "opentofu"
|
||||
}
|
||||
}
|
||||
|
||||
resource "hcloud_ssh_key" "deploy" {
|
||||
name = "${var.server_name}-deploy"
|
||||
public_key = trimspace(var.ssh_public_key)
|
||||
labels = local.common_labels
|
||||
}
|
||||
|
||||
resource "hcloud_primary_ip" "main" {
|
||||
name = "${var.server_name}-ipv4"
|
||||
location = var.location
|
||||
type = "ipv4"
|
||||
auto_delete = false
|
||||
delete_protection = var.enable_resource_protection
|
||||
labels = local.common_labels
|
||||
}
|
||||
|
||||
resource "hcloud_firewall" "main" {
|
||||
name = "${var.server_name}-firewall"
|
||||
labels = local.common_labels
|
||||
|
||||
rule {
|
||||
description = "SSH from configured administrator networks"
|
||||
direction = "in"
|
||||
protocol = "tcp"
|
||||
port = "22"
|
||||
source_ips = var.admin_cidrs
|
||||
}
|
||||
|
||||
rule {
|
||||
description = "HTTP for ACME challenge and HTTPS redirect"
|
||||
direction = "in"
|
||||
protocol = "tcp"
|
||||
port = "80"
|
||||
source_ips = [
|
||||
"0.0.0.0/0",
|
||||
"::/0",
|
||||
]
|
||||
}
|
||||
|
||||
rule {
|
||||
description = "HTTPS"
|
||||
direction = "in"
|
||||
protocol = "tcp"
|
||||
port = "443"
|
||||
source_ips = [
|
||||
"0.0.0.0/0",
|
||||
"::/0",
|
||||
]
|
||||
}
|
||||
|
||||
rule {
|
||||
description = "ICMP diagnostics"
|
||||
direction = "in"
|
||||
protocol = "icmp"
|
||||
source_ips = [
|
||||
"0.0.0.0/0",
|
||||
"::/0",
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
resource "hcloud_server" "main" {
|
||||
name = var.server_name
|
||||
image = "ubuntu-24.04"
|
||||
server_type = var.server_type
|
||||
location = var.location
|
||||
|
||||
ssh_keys = [hcloud_ssh_key.deploy.id]
|
||||
firewall_ids = [hcloud_firewall.main.id]
|
||||
|
||||
user_data = templatefile("${path.module}/cloud-init.yaml.tftpl", {
|
||||
deploy_user = var.deploy_user
|
||||
ssh_public_key = jsonencode(trimspace(var.ssh_public_key))
|
||||
})
|
||||
|
||||
public_net {
|
||||
ipv4_enabled = true
|
||||
ipv4 = hcloud_primary_ip.main.id
|
||||
ipv6_enabled = false
|
||||
}
|
||||
|
||||
delete_protection = var.enable_resource_protection
|
||||
rebuild_protection = var.enable_resource_protection
|
||||
shutdown_before_deletion = true
|
||||
|
||||
labels = local.common_labels
|
||||
}
|
||||
|
||||
resource "hcloud_volume" "routing_data" {
|
||||
name = "${var.server_name}-routing-data"
|
||||
location = var.location
|
||||
size = var.routing_volume_size_gb
|
||||
format = "ext4"
|
||||
delete_protection = var.enable_resource_protection
|
||||
labels = local.common_labels
|
||||
}
|
||||
|
||||
resource "hcloud_volume_attachment" "routing_data" {
|
||||
volume_id = hcloud_volume.routing_data.id
|
||||
server_id = hcloud_server.main.id
|
||||
automount = false
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
output "server_ipv4" {
|
||||
description = "Persistente öffentliche IPv4-Adresse des Watermaps-Servers."
|
||||
value = hcloud_primary_ip.main.ip_address
|
||||
}
|
||||
|
||||
output "server_id" {
|
||||
description = "Hetzner-ID des Servers."
|
||||
value = hcloud_server.main.id
|
||||
}
|
||||
|
||||
output "ssh_command" {
|
||||
description = "SSH-Befehl für den vorbereiteten Deploy-Benutzer."
|
||||
value = "ssh ${var.deploy_user}@${hcloud_primary_ip.main.ip_address}"
|
||||
}
|
||||
|
||||
output "dns_a_record" {
|
||||
description = "A-Record, der nach dem Apply manuell beim DNS-Provider angelegt werden soll."
|
||||
value = {
|
||||
type = "A"
|
||||
name = var.dns_name
|
||||
value = hcloud_primary_ip.main.ip_address
|
||||
}
|
||||
}
|
||||
|
||||
output "routing_data_mount_path" {
|
||||
description = "Persistenter Pfad für heruntergeladene PBFs und erzeugte Fahrrouten."
|
||||
value = "/srv/watermaps-data"
|
||||
}
|
||||
|
||||
output "routing_volume_id" {
|
||||
description = "Hetzner-ID des persistenten Routingdaten-Volumes."
|
||||
value = hcloud_volume.routing_data.id
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
provider "hcloud" {
|
||||
token = var.hcloud_token
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
# Diese Datei enthält ausschließlich Beispiele. Kopiere sie nach
|
||||
# terraform.tfvars und ersetze alle REPLACE_*-Werte.
|
||||
|
||||
hcloud_token = "REPLACE_WITH_HETZNER_CLOUD_READ_WRITE_TOKEN"
|
||||
|
||||
# Inhalt einer öffentlichen .pub-Datei, niemals den privaten Schlüssel eintragen.
|
||||
ssh_public_key = "ssh-ed25519 REPLACE_WITH_PUBLIC_KEY user@example"
|
||||
|
||||
# Auf deine aktuelle öffentliche IP begrenzen. Mehrere Netze sind möglich.
|
||||
admin_cidrs = [
|
||||
"203.0.113.10/32",
|
||||
]
|
||||
|
||||
server_name = "watermaps"
|
||||
dns_name = "watermaps.incoso.eu"
|
||||
location = "fsn1"
|
||||
server_type = "cx33"
|
||||
|
||||
deploy_user = "deploy"
|
||||
routing_volume_size_gb = 40
|
||||
|
||||
# Schützt Ressourcen vor versehentlicher Löschung über Console/API.
|
||||
enable_resource_protection = true
|
||||
@@ -0,0 +1,122 @@
|
||||
variable "hcloud_token" {
|
||||
description = "Read/Write API-Token des Hetzner-Cloud-Projekts."
|
||||
type = string
|
||||
sensitive = true
|
||||
nullable = false
|
||||
|
||||
validation {
|
||||
condition = length(trimspace(var.hcloud_token)) == 64 && !startswith(var.hcloud_token, "REPLACE_")
|
||||
error_message = "hcloud_token muss durch einen gültigen, 64 Zeichen langen Hetzner-Cloud-API-Token ersetzt werden."
|
||||
}
|
||||
}
|
||||
|
||||
variable "server_name" {
|
||||
description = "Name des Hetzner-Servers und Präfix der zugehörigen Ressourcen."
|
||||
type = string
|
||||
default = "watermaps"
|
||||
|
||||
validation {
|
||||
condition = can(regex("^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$", var.server_name))
|
||||
error_message = "server_name muss ein gültiger Hostname aus Kleinbuchstaben, Ziffern und Bindestrichen sein."
|
||||
}
|
||||
}
|
||||
|
||||
variable "dns_name" {
|
||||
description = "Öffentlicher DNS-Name. Der DNS-Eintrag selbst wird bewusst nicht von OpenTofu verwaltet."
|
||||
type = string
|
||||
default = "watermaps.incoso.eu"
|
||||
|
||||
validation {
|
||||
condition = can(regex("^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?(?:\\.[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?)+$", var.dns_name))
|
||||
error_message = "dns_name muss ein gültiger, vollständig qualifizierter DNS-Name sein."
|
||||
}
|
||||
}
|
||||
|
||||
variable "location" {
|
||||
description = "Hetzner-Cloud-Location für Server, Primary IP und Volume."
|
||||
type = string
|
||||
default = "fsn1"
|
||||
|
||||
validation {
|
||||
condition = length(trimspace(var.location)) > 0
|
||||
error_message = "location darf nicht leer sein."
|
||||
}
|
||||
}
|
||||
|
||||
variable "server_type" {
|
||||
description = "Hetzner-Cloud-Servertyp. cx33 ist für den initialen Routingdaten-Import konservativ gewählt."
|
||||
type = string
|
||||
default = "cx33"
|
||||
|
||||
validation {
|
||||
condition = length(trimspace(var.server_type)) > 0
|
||||
error_message = "server_type darf nicht leer sein."
|
||||
}
|
||||
}
|
||||
|
||||
variable "ssh_public_key" {
|
||||
description = "Öffentlicher SSH-Schlüssel für root und den Deploy-Benutzer."
|
||||
type = string
|
||||
nullable = false
|
||||
|
||||
validation {
|
||||
condition = (
|
||||
!strcontains(var.ssh_public_key, "REPLACE_") &&
|
||||
can(regex(
|
||||
"^(ssh-(ed25519|rsa)|ecdsa-sha2-nistp(256|384|521)|sk-ssh-ed25519@openssh\\.com|sk-ecdsa-sha2-nistp256@openssh\\.com) ",
|
||||
trimspace(var.ssh_public_key)
|
||||
))
|
||||
)
|
||||
error_message = "ssh_public_key muss ein gültiger öffentlicher OpenSSH-Schlüssel sein."
|
||||
}
|
||||
}
|
||||
|
||||
variable "admin_cidrs" {
|
||||
description = "IPv4- oder IPv6-CIDRs, aus denen SSH auf Port 22 erlaubt ist, zum Beispiel [\"203.0.113.10/32\"]."
|
||||
type = list(string)
|
||||
nullable = false
|
||||
|
||||
validation {
|
||||
condition = (
|
||||
length(var.admin_cidrs) > 0 &&
|
||||
alltrue([for cidr in var.admin_cidrs : can(cidrhost(cidr, 0))]) &&
|
||||
!contains(var.admin_cidrs, "203.0.113.10/32")
|
||||
)
|
||||
error_message = "admin_cidrs muss mindestens ein gültiges, echtes IPv4- oder IPv6-CIDR enthalten; die Beispieladresse muss ersetzt werden."
|
||||
}
|
||||
}
|
||||
|
||||
variable "deploy_user" {
|
||||
description = "Unprivilegierter Benutzer für Upload und Betrieb der Anwendung."
|
||||
type = string
|
||||
default = "deploy"
|
||||
|
||||
validation {
|
||||
condition = (
|
||||
var.deploy_user != "root" &&
|
||||
can(regex("^[a-z_][a-z0-9_-]{0,30}$", var.deploy_user))
|
||||
)
|
||||
error_message = "deploy_user muss ein gültiger Linux-Benutzername sein und darf nicht root heißen."
|
||||
}
|
||||
}
|
||||
|
||||
variable "routing_volume_size_gb" {
|
||||
description = "Größe des persistenten ext4-Volumes für Deutschland-/Niederlande-PBFs und den Routingindex."
|
||||
type = number
|
||||
default = 40
|
||||
|
||||
validation {
|
||||
condition = (
|
||||
var.routing_volume_size_gb >= 10 &&
|
||||
var.routing_volume_size_gb <= 10000 &&
|
||||
floor(var.routing_volume_size_gb) == var.routing_volume_size_gb
|
||||
)
|
||||
error_message = "routing_volume_size_gb muss eine ganze Zahl zwischen 10 und 10000 sein."
|
||||
}
|
||||
}
|
||||
|
||||
variable "enable_resource_protection" {
|
||||
description = "Aktiviert Hetzner-Löschschutz für Server, Primary IP und Volume."
|
||||
type = bool
|
||||
default = true
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
terraform {
|
||||
required_version = ">= 1.8.0, < 2.0.0"
|
||||
|
||||
required_providers {
|
||||
hcloud = {
|
||||
source = "hetznercloud/hcloud"
|
||||
version = "= 1.66.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user