I’ve written a couple of scripts to create servers on Hetzner over the years to facilitate small projects and experiementation.

My older scripts used the Hetzner CLI which was fine but I wanted a version without that dependency or any other dependencies. So, I rewrote it in bash without the CLI by using curl to call the webservices. Initially, I had used jq to work with the returned JSON but since the requirements were pretty simple, I refactored that out.

There are a few things that I’d like to improve (e.g., refactor it to reduce some of the code reptition, add some of the configuration features from my CLI script, optionally connect to the created server using SSH, etc.) but it’s working for now. Here it is:

#!/bin/bash

# vim: filetype=sh

set -euo pipefail
IFS=$'\n\t'

API_TOKEN=$(cat ~/.hetzner_api)

# define variables
name=""
location=""
server_type=""
image=""
key=""

# read the arguments
while getopts "n:l:t:i:k:" flag
do 
	case "${flag}" in 
		n) name=${OPTARG};;
		l) location=${OPTARG};;
		t) server_type=${OPTARG};;
		i) image=${OPTARG};;
		k) key=${OPTARG};;
	esac
done

# get a name for the server
if [[ -z "$name" ]]
then
	echo -n "Server name?: "
	read -r name
fi
echo "name: $name"


# get the list of locations
if [[ -z "$location" ]]
then
	loc_json=$(curl -s -H "Authorization: Bearer $API_TOKEN" \
		"https://api.hetzner.cloud/v1/locations")
	locations=( $(echo $loc_json | grep -o '"name": "[^"]*' | grep -o '[^"]*$') )
	# select a location from the list
	echo "location?"
	select location in "${locations[@]}"; do
		case $location in 
			*)
				break;;
		esac
	done
fi
echo "location: $location"

# get the list of server types
if [[ -z "$server_type" ]]
then
	server_json=$(curl -s -H "Authorization: Bearer $API_TOKEN" \
		"https://api.hetzner.cloud/v1/server_types")
	server_types=( $(echo $server_json | grep -o '"name": "[^"]*' | grep -o '[^"]*$') )
	# select a server type from the list
	echo "server type?"
	select server_type in "${server_types[@]}"; do
		case $server_type in 
			*)
				break;;
		esac
	done
fi
echo "server type: $server_type"

# get the list of images 
if [[ -z "$image" ]]
then
	images_json=$(curl -s -H "Authorization: Bearer $API_TOKEN" \
		"https://api.hetzner.cloud/v1/images?type=system")
	images=( $(echo $images_json | grep -o '"name": "[^"]*' | grep -o '[^"]*$') )
	
	# select an image  from the list
	echo "image?"
	select image in "${images[@]}"; do
		case $image in 
			*)
				break;;
		esac
	done
fi
echo "image: $image"

# get the list of ssh keys 
if [[ -z "$key" ]]
then
	keys_json=$(curl -s -H "Authorization: Bearer $API_TOKEN" \
		"https://api.hetzner.cloud/v1/ssh_keys")
	keys=( $(echo $keys_json | grep -o '"name": "[^"]*' | grep -o '[^"]*$') )
	
	# select a key from the list
	echo "ssh key?"
	select key in "${keys[@]}"; do
		case $key in 
			*)
				break;;
		esac
	done
fi
echo "$key"

# show values and confirm server creation
data=$(
cat <<EOF
{
	"name": "$name",
	"server_type": "$server_type",
	"image": "$image",
	"location": "$location",
	"ssh_keys": [
		"$key"
	]
}
EOF
)

echo "$data"
echo -n "Proceed? [y/n]: "
read -n 1 ans

if [[ "$ans" != "y" ]]; then
	exit
fi

# create the server and store the output in a file
echo "\ncreating $name..."
response=$(curl -s -H "Authorization: Bearer $API_TOKEN" \
	-H "Content-Type: application/json" \
	-d "$data" \
	"https://api.hetzner.cloud/v1/servers" \
)

destdir="${name}.json"
echo "$destdir"
echo "$response" > "$destdir"

exit