Skip to content

Terraform Overview

Terraform manages infrastructure outside the Kubernetes cluster: Cloudflare DNS/tunnels, Oracle Cloud compute, Backblaze B2 storage, AdGuard Home configuration, and database user/grant management.

Modules

terraform/
├── adguard/       # AdGuard Home DNS rewrites, filters, and config
├── cloudflare/    # DNS records, tunnels, WAF, zone settings
├── mariadb/       # MariaDB databases, users, and grants
├── oci/           # Oracle Cloud instance (k3s-oci-m3) and networking
├── postgres/      # PostgreSQL databases, users, and grants
└── backblaze/     # B2 buckets and scoped application keys

adguard

Manages AdGuard Home running on rpi-4b via the gmichels/adguard provider.

File Purpose
config.tf Global AdGuard settings (DNS, filtering, stats, query log)
dns_rewrites.tf Local DNS rewrites (LAN IPs + OCI public IP for Plex)
filters_deny.tf Blocklists
filters_allow.tf Allowlists
user_rules.tf Custom user rules
secrets.sops.yaml Encrypted: host, username, password, domain, OCI public IP

Known provider bug

gmichels/adguard v1.7.0 returns unknown values for tls.* fields after apply, which taints adguard_config. A lifecycle { ignore_changes = [tls] } workaround is in place. If the resource becomes tainted, run terraform untaint adguard_config.main before applying.

mariadb

Manages MariaDB databases, users, and grants via the petoju/mysql provider.

File Purpose
service_databases.tf Service databases, users, and grants — add names to local.service_databases
mysqld_exporter_user.tf Monitoring user (mysqld_exporter) with PROCESS, REPLICATION CLIENT, SELECT
providers.tf MySQL provider config (reads from SOPS)
secrets.sops.yaml Encrypted: endpoint, terraform credentials, per-user passwords

Add a new database by appending its name to local.service_databases in service_databases.tf.

postgres

Manages PostgreSQL databases, roles, and grants via the cyrilgdn/postgresql provider.

File Purpose
service_databases.tf Service roles, databases, and grants — add names to local.service_databases
read_only_users.tf Read-only roles (e.g. grafana) granted pg_read_all_data
providers.tf PostgreSQL provider config (reads host:port and credentials from SOPS)
secrets.sops.yaml Encrypted: host:port, terraform credentials, per-user passwords

Add a new service database by appending its name to local.service_databases in service_databases.tf. Read-only accounts go in read_only_users.tf.

Backend

State is stored in an S3-compatible backend (Backblaze B2, terraform bucket). The backend config is SOPS-encrypted:

# backend.sops.tfbackend contains bucket, endpoint, access_key, secret_key
sops -d ../backend.sops.tfbackend > ../.decrypted~backend.sops.tfbackend
terraform init -backend-config=../.decrypted~backend.sops.tfbackend

Decrypted intermediary

The .decrypted~backend.sops.tfbackend file is a temporary plaintext copy. It is gitignored (.decrypted~* pattern) and must never be committed.

Providers

Provider Version Purpose
carlpett/sops ~> 1.1 Read SOPS-encrypted files as Terraform data sources
gmichels/adguard ~> 1.7 AdGuard Home configuration
cloudflare/cloudflare ~> 5.0 Cloudflare resources
oracle/oci 8.10.0 Oracle Cloud Infrastructure resources
Backblaze/b2 ~> 0.12.1 Backblaze B2 buckets and application keys
petoju/mysql ~> 3.0 MariaDB databases, users, and grants
cyrilgdn/postgresql ~> 1.25 PostgreSQL databases, roles, and grants
hashicorp/random ~> 3.0 Random password generation

Running Terraform

cd terraform/<module>

# Decrypt backend config (required once per session)
sops -d ../backend.sops.tfbackend > ../.decrypted~backend.sops.tfbackend

# Init, plan, apply
terraform init -backend-config=../.decrypted~backend.sops.tfbackend
terraform plan
terraform apply

# Clean up
rm ../.decrypted~backend.sops.tfbackend

Never commit .decrypted~* files

These files contain plaintext secrets. They are gitignored by default, but double-check before committing.