# πŸš€ Provisioning Servers with railiance-infra This guide explains how adopted hosts and provider-managed Hetzner hosts share an inventory without sharing lifecycle behavior. --- ## πŸš€ Fast Path: Using the Helper Script Instead of manually editing `inventory/servers.yaml` and running `make tf-apply`, you can use the convenience script [`scripts/hcloud_new_server.sh`](../scripts/hcloud_new_server.sh). This script will: 1. Add the new host entry to `inventory/servers.yaml` 2. Decrypt your Hetzner API token with SOPS 3. Run Terraform init and plan 4. Apply only when the operator supplies the explicit `--apply` flag after reviewing the plan **Example:** ```bash scripts/hcloud_new_server.sh core-01 --type cpx11 --region nbg1 --role core # after review and the required approval: scripts/hcloud_new_server.sh core-01 --type cpx11 --region nbg1 --role core --apply ``` This will create a small cpx11 instance in the Nuremberg (nbg1) region, tagged with the role core. You can then connect directly: ```bash ssh admin@ ``` πŸ‘‰ The script is optional. You can always manage servers by editing inventory/servers.yaml and running make tf-apply instead. ## 1) Where you define servers All host identities live in **`inventory/servers.yaml`**. Run `make validate-inventory` before Terraform or Ansible. Adopted hosts have a provider address but no `provisioning` block. Provider-managed Hetzner hosts have no committed IP and use this shape: ```yaml servers: - name: core-01 provider: hetzner lifecycle_mode: provider-managed ssh_user: admin baseline_profile: ufw-managed provisioning: server_type: cpx21 region: nbg1 image: ubuntu-24.04 role: core labels: [core, wireguard, git] ``` > Tip: Keep **names stable**. Renaming a server in this file makes Terraform think the old one was destroyed and a new one should be created. --- ## 2) Two ways to add a server ### A) Edit YAML by hand (simple) Open `inventory/servers.yaml`, add a new entry, save, commit. ### B) Use the helper script (safe & quick) ```bash # requires scripts/new_host.py make new-host NAME=web-01 TYPE=cpx21 REGION=nbg1 ROLE=web # or directly: python3 scripts/new_host.py --name web-01 --type cpx21 --region nbg1 --role web ``` You can also do **add + provision in one step**: ```bash scripts/hcloud_new_server.sh web-01 --type cpx21 --region nbg1 --role web ``` --- ## 3) How Terraform uses your declaration The module at `terraform/hetzner/`: - Selects only records with `provider: hetzner` and `lifecycle_mode: provider-managed` - Registers your SSH key from `keys/admin_ssh.pub` - Injects **cloud-init** that sets up the `admin` user and basic hardening - Creates/updates/destroys servers to match the YAML Outputs include a map of server names β†’ IPv4 addresses. --- ## 4) Provision (create/update) The Hetzner API token is **SOPS-decryptable** at `secrets/hetzner-token.yaml`, field `hetzner.token`. It is decrypted only into the invoking process environment. Then run either: ```bash # plan and apply in separate steps make tf-plan APPROVE_TF_APPLY=YES make tf-apply ``` or the end-to-end convenience: ```bash APPROVE_TF_APPLY=YES make apply # terraform apply + Ansible bootstrap ``` If you used the one-shot script: ```bash scripts/hcloud_new_server.sh web-01 --type cpx21 --region nbg1 --role web ``` Terraform will print the new servers’ IPv4 addresses at the end. --- ## 5) Connect & converge Connect via SSH: ```bash ssh admin@ ``` Run Ansible base bootstrap (if not using `make apply`): ```bash make ansible-bootstrap ``` --- ## 6) Destroy (tear down) To remove all servers managed by this repo: ```bash APPROVE_TF_DESTROY=DESTROY-MANAGED-HETZNER make tf-destroy ``` To remove one **provider-managed Hetzner** server, delete its entry from `inventory/servers.yaml`, review the plan, obtain the required approval, then: ```bash APPROVE_TF_APPLY=YES make tf-apply ``` Terraform will destroy the missing managed Hetzner server and leave others intact. Adopted Host Europe records are never Terraform resources. --- ## 7) Notes & conventions - **Idempotent:** You can run `make apply` repeatedly; Terraform converges infra, Ansible converges config. - **SSH keys:** Ensure `keys/admin_ssh.pub` exists before provisioning. - **Secret token:** The Hetzner API token is `secrets/hetzner-token.yaml`, field `hetzner.token`, and must remain SOPS-encrypted. - **Cloud-init delay:** Allow ~30–60s after creation for first-boot tasks before first SSH. - **Labels & role:** `labels` are freeform tags; `role` can drive Ansible plays as you grow.