Let your servers join your Tailscale tailnet with Terraform and cloud-init
First generate an oauth client credentials for use in Terraform by going to https://login.tailscale.com/admin/settings/oauth and clicking “Generate oauth client”:
Note down your oauth_client_id
and you oauth_client_secret
.
Inside Terraform first add the Tailscale provider:
terraform {
required_providers {
tailscale = {
source = "tailscale/tailscale"
version = "0.16.1"
}
}
}
After running terraform init
, you can then create a file named tailscale.tf
and insert this:
provider "tailscale" {
oauth_client_id = ""
oauth_client_secret = ""
}
resource "tailscale_tailnet_key" "hourlykey" {
reusable = true
ephemeral = true
preauthorized = true
expiry = 3600
description = "Hourly key Terraform managed"
tags = ["tag:server"]
}
Now Terraform will automatically create tailnet keys that works just for an hour, and they will automatically expire, but give you enough time to bootstrap servers and for them to join your Tailnet, neat!
So how do we use it? #
Most providers allow for a user_data
configuration option that uses cloud-init, some commands that will be run when the server boots:
resource "digitalocean_droplet" "server" {
user_data = <<EOF
#cloud-config
runcmd:
- tailscale up --authkey ${tailscale_tailnet_key.hourlykey.key} --accept-routes --accept-dns
EOF
Some people like to add a command that also downloads the Tailscale client, this means that your Tailscale will be up to date, but I don’t like the idea of installing software when I boot up servers, instead I prebake the Tailscale installation into my Packer images. But if you’d like to setup Tailscale as part of cloud-init you do this:
resource "digitalocean_droplet" "server" {
user_data = <<EOF
#cloud-config
runcmd:
- ['sh', '-c', 'curl -fsSL https://tailscale.com/install.sh | sh']
- tailscale up --authkey ${tailscale_tailnet_key.hourlykey.key} --accept-routes --accept-dns
EOF
So now everything is great, right? #
Not so fast! Because what happens if we run Terraform an hour later after our key have expired? Terraform will try and replace our servers with new servers using the newly generated Tailscale key!
The way to avoid is to ignore changes to user_data
, so the final configuration looks like this:
resource "digitalocean_droplet" "server" {
user_data = <<EOF
#cloud-config
runcmd:
- ['sh', '-c', 'curl -fsSL https://tailscale.com/install.sh | sh']
- tailscale up --authkey ${tailscale_tailnet_key.hourlykey.key} --accept-routes --accept-dns
EOF
lifecycle {
ignore_changes = [
user_data
]
}