2025-09-14 00:48:55 +02:00
|
|
|
|
#!/usr/bin/env bash
|
|
|
|
|
|
# hcloud_new_server.sh — Add a host to inventory and provision it on Hetzner
|
|
|
|
|
|
# Usage:
|
2026-08-23 12:02:23 +02:00
|
|
|
|
# scripts/hcloud_new_server.sh <NAME> [options] [--apply]
|
2025-09-14 00:48:55 +02:00
|
|
|
|
#
|
|
|
|
|
|
# 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"
|
2026-08-23 12:02:23 +02:00
|
|
|
|
APPLY=false
|
2025-09-14 00:48:55 +02:00
|
|
|
|
|
|
|
|
|
|
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;;
|
2026-08-23 12:02:23 +02:00
|
|
|
|
--apply) APPLY=true; shift;;
|
2025-09-14 00:48:55 +02:00
|
|
|
|
*) fail "Unknown arg: $1 (usage: scripts/hcloud_new_server.sh <NAME> [--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 <NAME> [--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" \
|
2026-08-23 12:02:23 +02:00
|
|
|
|
--user "$USER" \
|
|
|
|
|
|
--reuse-existing
|
2025-09-14 00:48:55 +02:00
|
|
|
|
|
|
|
|
|
|
ok "Inventory updated: $NAME → inventory/servers.yaml"
|
|
|
|
|
|
|
|
|
|
|
|
# --- Decrypt Hetzner token and apply Terraform ---
|
2026-08-23 12:02:23 +02:00
|
|
|
|
HCLOUD_TOKEN="$(sops -d --extract '["hetzner"]["token"]' secrets/hetzner-token.yaml 2>/dev/null)"
|
|
|
|
|
|
[[ -n "$HCLOUD_TOKEN" ]] || fail "Could not decrypt hetzner.token from secrets/hetzner-token.yaml. Ensure SOPS_AGE_KEY or keys.txt is set."
|
2025-09-14 00:48:55 +02:00
|
|
|
|
|
|
|
|
|
|
pushd terraform/hetzner >/dev/null
|
|
|
|
|
|
|
|
|
|
|
|
terraform init -upgrade
|
2026-08-23 12:02:23 +02:00
|
|
|
|
export TF_VAR_hcloud_token="$HCLOUD_TOKEN"
|
|
|
|
|
|
terraform plan
|
|
|
|
|
|
|
|
|
|
|
|
if [[ "$APPLY" != true ]]; then
|
|
|
|
|
|
info "Plan complete; no provider mutation performed. Re-run with --apply only after review and approval."
|
|
|
|
|
|
popd >/dev/null
|
|
|
|
|
|
exit 0
|
|
|
|
|
|
fi
|
|
|
|
|
|
|
2025-09-14 00:48:55 +02:00
|
|
|
|
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."
|