From bc0ea33555715783d68f2f427b81b50a6d6ba594 Mon Sep 17 00:00:00 2001 From: Bernd Worsch Date: Sun, 14 Sep 2025 00:48:55 +0200 Subject: [PATCH] feat: script to set up servers at hetzner --- scripts/hcloud_new_server.sh | 94 ++++++++++++++++++++++++++++++++++++ 1 file changed, 94 insertions(+) create mode 100644 scripts/hcloud_new_server.sh diff --git a/scripts/hcloud_new_server.sh b/scripts/hcloud_new_server.sh new file mode 100644 index 0000000..cd10f15 --- /dev/null +++ b/scripts/hcloud_new_server.sh @@ -0,0 +1,94 @@ +#!/usr/bin/env bash +# hcloud_new_server.sh — Add a host to inventory and provision it on Hetzner +# Usage: +# scripts/hcloud_new_server.sh [--type cpx11] [--region nbg1] [--role web] [--image ubuntu-24.04] [--user admin] +# +# Prereqs: +# - age + SOPS installed, with access to decrypt your Hetzner token +# - terraform installed +# - Python3 + PyYAML (for scripts/new_host.py) +# +set -euo pipefail + +fail() { echo "❌ $*" >&2; exit 1; } +ok() { echo "✔ $*"; } +info() { echo "ℹ $*"; } + +# --- Parse args --- +NAME="${1:-}" +shift || true + +TYPE="cpx21" +REGION="nbg1" +ROLE="generic" +IMAGE="ubuntu-24.04" +USER="admin" + +while [[ $# -gt 0 ]]; do + case "$1" in + --type) TYPE="$2"; shift 2;; + --region) REGION="$2"; shift 2;; + --role) ROLE="$2"; shift 2;; + --image) IMAGE="$2"; shift 2;; + --user) USER="$2"; shift 2;; + *) fail "Unknown arg: $1 (usage: scripts/hcloud_new_server.sh [--type cpx11] [--region nbg1] [--role web] [--image ubuntu-24.04] [--user admin])";; + esac +done + +[[ -n "$NAME" ]] || fail "Missing NAME. Usage: scripts/hcloud_new_server.sh [--type ...] ..." + +# --- Sanity checks --- +command -v terraform >/dev/null || fail "terraform not found" +command -v sops >/dev/null || fail "sops not found" +command -v python3 >/dev/null || fail "python3 not found" +python3 -c "import yaml" 2>/dev/null || fail "PyYAML not installed for python3 (pip install pyyaml)" + +if [[ ! -f "scripts/new_host.py" ]]; then + fail "scripts/new_host.py not found (expected in repo)." +fi + +if [[ ! -f "keys/admin_ssh.pub" ]]; then + info "keys/admin_ssh.pub missing. Hetzner will still inject your project key if configured, but you may want to add one." +fi + +# --- Add host to inventory --- +python3 scripts/new_host.py \ + --name "$NAME" \ + --type "$TYPE" \ + --region "$REGION" \ + --role "$ROLE" \ + --image "$IMAGE" \ + --user "$USER" + +ok "Inventory updated: $NAME → inventory/servers.yaml" + +# --- Decrypt Hetzner token and apply Terraform --- +HCLOUD_TOKEN="$(sops -d --extract '["ops"]["hcloud_token"]' inventory/group_vars/secrets.sops.yaml 2>/dev/null || true)" +[[ -n "$HCLOUD_TOKEN" ]] || fail "Could not decrypt ops.hcloud_token from inventory/group_vars/secrets.sops.yaml. Ensure SOPS_AGE_KEY or keys.txt is set and token exists." + +pushd terraform/hetzner >/dev/null + +terraform init -upgrade +export HCLOUD_TOKEN +terraform apply -auto-approve + +# Try to show IP of the created host +if command -v jq >/dev/null; then + IP="$(terraform output -json servers | jq -r --arg n "$NAME" '.[$n] // empty')" + if [[ -n "$IP" && "$IP" != "null" ]]; then + ok "Server $NAME IPv4: $IP" + echo "" + echo "SSH quick check (if admin key injected):" + echo " ssh admin@$IP" + else + info "Could not extract IP for $NAME (jq path empty). Full outputs:" + terraform output + fi +else + info "jq not found. Terraform outputs:" + terraform output +fi + +popd >/dev/null + +ok "Provisioning complete."