Incrementally Creating A Base System

Technical Background

Infrastructure as Code (IaC) tools like Terraform enable declarative management of cloud resources. Instead of manually provisioning servers via a graphical interface, Terraform allows you to define your infrastructure in version-controlled configuration files. These configurations can be applied repeatedly and modified incrementally, ensuring predictable and reproducible deployments.

For this exercise, the Hetzner Cloud Provider is used to create a Debian-based server. The workflow covers:

  • Creating a minimal configuration
  • Securing SSH access using firewall rules
  • Switching from insecure password-based login to key-based authentication
  • Preventing accidental exposure of secrets (API tokens) in version control
  • Adding Terraform outputs to display server-specific data (IP address, datacenter)

Note

Hetzner sends an email containing login credentials (IP + root password) if no SSH key is provided when creating a server. This is a fallback security measure. Using SSH keys avoids password-based login entirely and is the recommended best practice.

Solution

Minimal Terraform Setup

  1. Create a new project directory:
mkdir terraform-hello && cd terraform-hello
  1. Create main.tf containing:
resource "hcloud_server" "web" {
  name         = "web"
  image        = "debian-12"
  server_type  = "cx22"
  firewall_ids = [hcloud_firewall.sshFw.id]
  ssh_keys     = [hcloud_ssh_key.loginUser.id]
}
  1. Initialize and apply:
terraform init
terraform plan
terraform apply

Note

The console output confirms a successful server creation.

After a successful run, Hetzner sends an email containing the server’s IP address and a randomly generated root password only when no SSH key is provided during server creation.

This ensures the user can still access the server securely after initial provisioning. Once you log in for the first time, you are required to change the root password. When using SSH keys instead, this email is not needed because authentication happens via your private key.

Secure API Tokens

  1. Create secrets.auto.tfvars containing:
api_token_hetzner = "hetzner_api_token"

loginUser_name = "your_email"
  1. Create variables.tf containing:
variable "api_token_hetzner" {
  type = string
  sensitive = true
}

variable "loginUser_name" {
  type = string
  sensitive = true
}
  1. Create providers.tf containing:
terraform {
  required_providers {
    hcloud = {
      source = "hetznercloud/hcloud"
    }
  }
  required_version = ">= 0.13"
}

provider "hcloud" {
  token = var.api_token_hetzner
}
  1. Add secrets.auto.tfvars to .gitignore:
echo "secrets.auto.tfvars" >> .gitignore

Warning

Never commit raw API tokens to version control. Using a separate untracked file is safer.

SSH Key Login and Firewall

  1. Generate an SSH key (if not existing):
ssh-keygen -t ed25519 -C "your_email@example.com"
  1. Add SSH key resource in main.tf:
resource "hcloud_ssh_key" "loginUser" {
  name = var.loginUser_name
  public_key = file("~/.ssh/id_ed25519.pub")
}
  1. Create network.tf containing:
resource "hcloud_firewall" "sshFw" {
  name = "ssh-firewall"
  rule {
    direction = "in"
    protocol  = "tcp"
    port      = "22"
    source_ips = ["0.0.0.0/0", "::/0"]
  }
}

Important

This needs to be added to have SSH access.

  1. Attach firewall and SSH key to server in main.tf:
resource "hcloud_server" "web" {
  name         = "web"
  image        = "debian-12"
  server_type  = "cx22"
  firewall_ids = [hcloud_firewall.sshFw.id]
  ssh_keys     = [hcloud_ssh_key.loginUser.id]
}
  1. Apply configuration:
terraform apply
  1. Test SSH login:
ssh root@<your-server_ip>

Success

You can now log in without a password using your private key. The password email from Hetzner is no longer required.

Add Outputs

  1. Create outputs.tf:
output "web_ip_addr" {
  value       = hcloud_server.web.ipv4_address
  description = "The server's IPv4 address"
}

output "web_datacenter" {
  value       = hcloud_server.web.datacenter
  description = "The server's datacenter"
}
  1. Re-apply configuration:
terraform apply
terraform output

Success

Expected output:

hello_datacenter = "hel1-dc2" hello_ip_addr = "95.217.154.104"

Info

Outputs make it easier to integrate with scripts and documentation.

Debian Introduction

Debian References

dpkg commands

PPA for Ubuntu

Terraform Introduction

Terraform Reasons

Terraform Installation

Terraform CLI Docs

Managing Terraform Secrets

VNC

Terraform Hetzner Docs

Terraform Outputs

VCS

Terraform Variable