diff --git a/terraform/environments/offsite/backend.tf b/terraform/environments/offsite/backend.tf new file mode 100644 index 0000000..72e0c8f --- /dev/null +++ b/terraform/environments/offsite/backend.tf @@ -0,0 +1,4 @@ +# Terraform state: LOCAL, on the control node (like the Proxmox envs; ADR-006). +# askari survives a homelab outage by design, so a lost state is recovered by +# `terraform import` of the running server — not a rebuild. Back the state up with +# the control node (ADR-022). diff --git a/terraform/environments/offsite/main.tf b/terraform/environments/offsite/main.tf new file mode 100644 index 0000000..5e64217 --- /dev/null +++ b/terraform/environments/offsite/main.tf @@ -0,0 +1,19 @@ +# offsite/main.tf — off-site Hetzner hosts. Terraform owns VM existence (ADR-006, +# generalized to Hetzner). ALWAYS `make tf-plan TF_ENV=offsite` and review before +# `make tf-apply TF_ENV=offsite`. + +module "askari" { + source = "../../modules/hetzner_vm" + + name = "askari" + server_type = "cax11" # ARM, 2 vCPU / 4 GB + location = "hel1" # Helsinki + image = "debian-13" + ansible_ssh_pubkey = var.ansible_ssh_pubkey + ssh_admin_cidrs = var.ssh_admin_cidrs + labels = { + env = "offsite" + group = "offsite_hosts" + managed-by = "terraform" + } +} diff --git a/terraform/environments/offsite/outputs.tf b/terraform/environments/offsite/outputs.tf new file mode 100644 index 0000000..331a024 --- /dev/null +++ b/terraform/environments/offsite/outputs.tf @@ -0,0 +1,9 @@ +output "vms" { + description = "Hostname -> IP and Ansible group — consumed by make tf-inventory-offsite" + value = { + askari = { + ip = module.askari.ipv4_address + group = "offsite_hosts" + } + } +} diff --git a/terraform/environments/offsite/providers.tf b/terraform/environments/offsite/providers.tf new file mode 100644 index 0000000..2837a18 --- /dev/null +++ b/terraform/environments/offsite/providers.tf @@ -0,0 +1,15 @@ +# verified: hetznercloud/hcloud 1.65.0 · debian-13 image · cax11@hel1 · terraform-registry · 2026-06-14 +terraform { + required_version = ">= 1.9" + + required_providers { + hcloud = { + source = "hetznercloud/hcloud" + version = "~> 1.65" + } + } +} + +provider "hcloud" { + token = var.hcloud_token +} diff --git a/terraform/environments/offsite/terraform.tfvars.example b/terraform/environments/offsite/terraform.tfvars.example new file mode 100644 index 0000000..b7f5bc0 --- /dev/null +++ b/terraform/environments/offsite/terraform.tfvars.example @@ -0,0 +1,10 @@ +# offsite environment — non-secret values. Copy to terraform.tfvars and fill in. +# +# Secret is exported as an env var (never in this file); the make tf-* targets do this +# automatically for TF_ENV=offsite, sourcing vault.hetzner.token: +# export TF_VAR_hcloud_token="...from vault.hetzner.token..." +# +# State is local (see backend.tf). + +ansible_ssh_pubkey = "ssh-ed25519 AAAA... ansible@ubongo" +ssh_admin_cidrs = ["10.20.10.151/32"] # ubongo's LAN address (ADR-021) diff --git a/terraform/environments/offsite/variables.tf b/terraform/environments/offsite/variables.tf new file mode 100644 index 0000000..6a0ff3b --- /dev/null +++ b/terraform/environments/offsite/variables.tf @@ -0,0 +1,15 @@ +variable "hcloud_token" { + description = "Hetzner Cloud API token — set via TF_VAR_hcloud_token (from vault.hetzner.token)" + type = string + sensitive = true +} + +variable "ansible_ssh_pubkey" { + description = "ubongo's control SSH public key, provisioned for the ansible user" + type = string +} + +variable "ssh_admin_cidrs" { + description = "Source CIDRs allowed to SSH askari (ubongo's address/32)" + type = list(string) +}