Skip to content

Terraform

Terraform is used to manage the network and hardware settings.

  • Directoryterraform/ network/hardware
    • backend.tf configures the state file
    • dns.tf configures domain stuff
    • providers.tf defines the providers I’m using
    • vars.tf sets what to pass in when running Terraform
    • web.tf configures my web server
terraform/dns.tf
locals {
default_ttl = 1 # auto
default_proxied = false
}
data "cloudflare_zones" "zones" {
name = local.domain
}
resource "cloudflare_dns_record" "record" {
for_each = local.records
zone_id = data.cloudflare_zones.zones.result[0].id
name = each.value.name
type = each.value.type
content = each.value.content
ttl = try(each.value.ttl, local.default_ttl)
proxied = try(each.value.proxied, local.default_proxied)
priority = try(each.value.priority, null)
}

Here Terraform uses Cloudflare to set all the records for my domain.

terraform/web.tf
resource "mythicbeasts_pi" "web" {
identifier = var.pi_identifier
disk_size = 50
model = 4
memory = 4096
}
resource "mythicbeasts_proxy_endpoint" "endpoint" {
for_each = local.web_subdomains
domain = local.domain
hostname = each.value
address = mythicbeasts_pi.web.ip
site = "all"
proxy_protocol = true
}
resource "ansible_host" "web" {
name = "pi"
groups = ["web"]
variables = {
ansible_host = "ssh.${mythicbeasts_pi.web.identifier}.hostedpi.com"
ansible_port = mythicbeasts_pi.web.ssh_port
ansible_user = "ops"
}
}

Here Terraform requests the Pi and configures the Proxy endpoints using its IP address.

It also creates an ansible_host for the Pi so that Ansible can target it to configure it.

The state file, that records the current state of the infrastructure that Terraform manages, is hosted on MinIO running on my NAS in my homelab.

This is configured using:

terraform/backend.tf
terraform {
backend "s3" {
bucket = "tfstate"
key = "paultibbetts.uk.tfstate"
region = "main"
skip_credentials_validation = true
skip_requesting_account_id = true
skip_metadata_api_check = true
skip_region_validation = true
use_path_style = true
}
}

The variables that are my setup go in terraform/locals.tf:

terraform/locals.tf
locals {
domain = "paultibbetts.uk"
web_subdomains = {
apex = "@"
www = "www"
infra = "infra"
dev = "dev"
}
proxy_records = {
for key, host in local.web_subdomains : key => {
type = "CNAME"
name = host
content = "proxy.mythic-beasts.com"
}
}
records = merge(
local.proxy_records,
{
micro = {
type = "CNAME"
name = "micro"
content = "eu.micro.blog"
}
atproto = {
type = "TXT"
name = "_atproto"
content = "did=did:plc:adwdgyaga5q2psrjbvo4pndr"
}
github_pages_challenge = {
type = "TXT"
name = "_github-pages-challenge-paultibbetts"
content = "0d468c5988c95ab09a88f0c494dcb9"
}
mythic_beasts_challenge = {
type = "TXT"
name = "_mythic-beasts-challenge"
content = "T7eS19QKZV6VOo0og5sn2OmKDoem4dLL7IgT9tJLT7c"
}
openai_domain_verification = {
type = "TXT"
name = "@"
content = "openai-domain-verification=dv-CPJLyYLfDZ990DDlefMkxdkN"
}
# Apple iCloud email
# https://support.apple.com/en-gb/102374
apple_domain_verificaton = {
type = "TXT"
name = "@"
content = "apple-domain=JTK5xOaQo9cwujIv"
}
spf = {
type = "TXT"
name = "@"
content = "v=spf1 include:icloud.com ~all"
}
mx_icloud_01 = {
type = "MX"
name = "@"
content = "mx01.mail.icloud.com"
priority = 10
}
mx_icloud_02 = {
type = "MX"
name = "@"
content = "mx02.mail.icloud.com"
priority = 10
}
dkim_sig1 = {
type = "CNAME"
name = "sig1._domainkey"
content = "sig1.dkim.paultibbetts.uk.at.icloudmailadmin.com"
}
}
)
}

Terraform needs a few secrets passing to it to work.

Because it manages the domain using Cloudflare it needs a Cloudflare API token passing to it.

MinIO, which is where the state file is hosted, also needs to be told the key to use.

These secrets are exported as environment variables and read by Terraform:

Terminal window
source secrets.sh

where secrets.sh:

terraform/secrets.sh
export TF_VAR_cloudflare_api_token=$(pass cloudflare_api_token_paultibbettsuk)
export AWS_ENDPOINT_URL_S3=$(pass minio_endpoint)
export AWS_ACCESS_KEY_ID=$(pass minio_access_key)
export AWS_SECRET_ACCESS_KEY=$(pass minio_secret_key)
export MYTHICBEASTS_KEYID=$(pass mythicbeasts_keyid)
export MYTHICBEASTS_SECRET=$(pass mythicbeasts_secret)

The script grabs the secrets from a pass vault before exporting them for future commands to use.