fix: slight tweaks (#103)
Some checks are pending
Deploy (on merge) / Discover hosts (push) Waiting to run
Deploy (on merge) / Deploy → (push) Blocked by required conditions
Terraform / Plan (push) Waiting to run
Terraform / Apply (push) Blocked by required conditions

* fix: slight tweaks

* remove vendor
This commit is contained in:
Rasmus Wejlgaard 2026-05-09 20:49:46 +01:00 committed by GitHub
parent b5d5537c1f
commit 06552c5b75
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 84 additions and 244 deletions

View file

@ -4,19 +4,34 @@ Infrastructure-as-code for cloud and edge services. Uses [OpenTofu](https://open
## What's managed
- **Cloudflare DNS** — All `pez.sh` records (A, CNAME, MX, TXT)
- **Hetzner Cloud** — Two servers (`nuremberg-a`, `helsinki-a`), firewalls, and DNS for `pez.sh`
- **Grafana Cloud** — Stack, dashboards, synthetic monitoring checks, alert rules, Fleet collectors and pipelines
- **PagerDuty** — Service, escalation policy, and Grafana integration
## CI/CD
## Secrets
The original GitHub Actions workflow (`apply.yml`) ran plan on push to master, then applied with manual approval via a `prod` environment gate. This workflow lived in the standalone `pez-terraform` repo and would need adapting for the monorepo structure (e.g., path-filtered triggers).
Secrets are stored encrypted in `secrets.enc.yaml` via [SOPS](https://github.com/getsops/sops) and decrypted at plan/apply time into `secrets.yaml`. The Makefile handles decryption automatically.
Required secret keys: `hetzner_token`, `grafana_cloud_access_policy`, `grafana_synthetic_monitoring_access_token`, `grafana_fleet_management_auth`, `grafana_service_account_token`, `pagerduty_token`, `plex_token`, `backblaze_key_id`.
## State
State is stored in a Backblaze B2 bucket (`pez-infra-tfstate`) using an S3-compatible backend. Credentials are read from `AWS_ACCESS_KEY_ID` / `AWS_SECRET_ACCESS_KEY` environment variables.
## Usage
```sh
make init # initialize providers and backend
make plan # preview changes
make apply # apply changes
make fmt # format all .tf files
```
## Provider versions
| Provider | Source | Version |
|----------|--------|---------|
| Cloudflare | `cloudflare/cloudflare` | `~> 5.18` |
| Hetzner Cloud | `hetznercloud/hcloud` | `~> 1.45` |
| Grafana | `grafana/grafana` | `~> 4.35` |
| PagerDuty | `pagerduty/pagerduty` | `~> 2.2` |
| OpenTofu | — | `>= 1.6.0` |
## Migrated from
This directory replaces the standalone [`pez-terraform`](https://github.com/RWejlgaard/pez-terraform) repo.

View file

@ -1,83 +0,0 @@
resource "grafana_synthetic_monitoring_check_alerts" "pez_sh" {
check_id = grafana_synthetic_monitoring_check.pez_sh.id
alerts = [
{
name = "ProbeFailedExecutionsTooHigh"
threshold = 3
period = "30m"
runbook_url = ""
}
]
}
resource "grafana_synthetic_monitoring_check_alerts" "pez_solutions" {
check_id = grafana_synthetic_monitoring_check.pez_solutions.id
alerts = [
{
name = "ProbeFailedExecutionsTooHigh"
threshold = 3
period = "30m"
runbook_url = ""
}
]
}
resource "grafana_synthetic_monitoring_check_alerts" "jellyfin" {
check_id = grafana_synthetic_monitoring_check.jellyfin.id
alerts = [
{
name = "ProbeFailedExecutionsTooHigh"
threshold = 3
period = "30m"
runbook_url = ""
}
]
}
resource "grafana_synthetic_monitoring_check_alerts" "plex" {
check_id = grafana_synthetic_monitoring_check.plex.id
alerts = [
{
name = "ProbeFailedExecutionsTooHigh"
threshold = 3
period = "30m"
runbook_url = ""
}
]
}
resource "grafana_synthetic_monitoring_check_alerts" "request" {
check_id = grafana_synthetic_monitoring_check.request.id
alerts = [
{
name = "ProbeFailedExecutionsTooHigh"
threshold = 3
period = "30m"
runbook_url = ""
}
]
}
resource "grafana_synthetic_monitoring_check_alerts" "jellyfin-requests" {
check_id = grafana_synthetic_monitoring_check.jellyfin-requests.id
alerts = [
{
name = "ProbeFailedExecutionsTooHigh"
threshold = 3
period = "30m"
runbook_url = ""
}
]
}
resource "grafana_synthetic_monitoring_check_alerts" "git" {
check_id = grafana_synthetic_monitoring_check.git.id
alerts = [
{
name = "ProbeFailedExecutionsTooHigh"
threshold = 3
period = "30m"
runbook_url = ""
}
]
}

View file

@ -1,11 +1,31 @@
resource "grafana_synthetic_monitoring_check" "pez_sh" {
job = "pez.sh"
target = "https://pez.sh"
locals {
probe_london = 14
check_frequency = 600000
check_timeout = 3000
synthetic_checks = {
pez_sh = { job = "pez.sh", target = "https://pez.sh", headers = [] }
pez_solutions = { job = "pez.solutions", target = "https://pez.solutions", headers = [] }
jellyfin = { job = "jellyfin.pez.sh", target = "https://jellyfin.pez.sh", headers = [] }
plex = { job = "plex.pez.sh", target = "https://plex.pez.sh", headers = ["X-Plex-Token:${var.plex_token}"] }
request = { job = "request.pez.sh", target = "https://request.pez.sh", headers = [] }
jellyfin_requests = { job = "jellyfin-requests.pez.sh", target = "https://jellyfin-requests.pez.sh", headers = [] }
git = { job = "git.pez.sh", target = "https://git.pez.sh", headers = [] }
}
}
resource "grafana_synthetic_monitoring_check" "this" {
for_each = local.synthetic_checks
job = each.value.job
target = each.value.target
enabled = true
probes = [14] # 14 = London, UK
probes = [local.probe_london]
frequency = local.check_frequency
timeout = local.check_timeout
settings {
http {
method = "GET"
headers = each.value.headers
compression = "none"
fail_if_not_ssl = true
ip_version = "V4"
@ -13,142 +33,20 @@ resource "grafana_synthetic_monitoring_check" "pez_sh" {
valid_status_codes = ["200"]
}
}
frequency = 600000
timeout = 3000
lifecycle {
ignore_changes = [settings]
}
}
resource "grafana_synthetic_monitoring_check" "pez_solutions" {
job = "pez.solutions"
target = "https://pez.solutions"
enabled = true
probes = [14] # 14 = London, UK
settings {
http {
method = "GET"
compression = "none"
fail_if_not_ssl = true
ip_version = "V4"
valid_http_versions = ["HTTP/2.0", "HTTP/1.1", "HTTP/1.0"]
valid_status_codes = ["200"]
}
}
frequency = 600000
timeout = 3000
lifecycle {
ignore_changes = [settings]
}
}
resource "grafana_synthetic_monitoring_check" "jellyfin" {
job = "jellyfin.pez.sh"
target = "https://jellyfin.pez.sh"
enabled = true
probes = [14] # 14 = London, UK
settings {
http {
method = "GET"
compression = "none"
fail_if_not_ssl = true
ip_version = "V4"
valid_http_versions = ["HTTP/2.0", "HTTP/1.1", "HTTP/1.0"]
valid_status_codes = ["200"]
}
}
frequency = 600000
timeout = 3000
lifecycle {
ignore_changes = [settings]
}
}
resource "grafana_synthetic_monitoring_check" "plex" {
job = "plex.pez.sh"
target = "https://plex.pez.sh"
enabled = true
probes = [14] # 14 = London, UK
settings {
http {
method = "GET"
headers = ["X-Plex-Token:${var.plex_token}"]
compression = "none"
fail_if_not_ssl = true
ip_version = "V4"
valid_http_versions = ["HTTP/2.0", "HTTP/1.1", "HTTP/1.0"]
valid_status_codes = ["200"]
}
}
frequency = 600000
timeout = 3000
lifecycle {
ignore_changes = [settings]
}
}
resource "grafana_synthetic_monitoring_check" "request" {
job = "request.pez.sh"
target = "https://request.pez.sh"
enabled = true
probes = [14] # 14 = London, UK
settings {
http {
method = "GET"
compression = "none"
fail_if_not_ssl = true
ip_version = "V4"
valid_http_versions = ["HTTP/2.0", "HTTP/1.1", "HTTP/1.0"]
valid_status_codes = ["200"]
}
}
frequency = 600000
timeout = 3000
lifecycle {
ignore_changes = [settings]
}
}
resource "grafana_synthetic_monitoring_check" "jellyfin-requests" {
job = "jellyfin-requests.pez.sh"
target = "https://jellyfin-requests.pez.sh"
enabled = true
probes = [14] # 14 = London, UK
settings {
http {
method = "GET"
compression = "none"
fail_if_not_ssl = true
ip_version = "V4"
valid_http_versions = ["HTTP/2.0", "HTTP/1.1", "HTTP/1.0"]
valid_status_codes = ["200"]
}
}
frequency = 600000
timeout = 3000
lifecycle {
ignore_changes = [settings]
}
}
resource "grafana_synthetic_monitoring_check" "git" {
job = "git.pez.sh"
target = "https://git.pez.sh"
enabled = true
probes = [14] # 14 = London, UK
settings {
http {
method = "GET"
compression = "none"
fail_if_not_ssl = true
ip_version = "V4"
valid_http_versions = ["HTTP/2.0", "HTTP/1.1", "HTTP/1.0"]
valid_status_codes = ["200"]
}
}
frequency = 600000
timeout = 3000
lifecycle {
ignore_changes = [settings]
resource "grafana_synthetic_monitoring_check_alerts" "this" {
for_each = grafana_synthetic_monitoring_check.this
check_id = each.value.id
alerts = [
{
name = "ProbeFailedExecutionsTooHigh"
threshold = 3
period = "30m"
runbook_url = ""
}
]
}

View file

@ -1,4 +1,5 @@
variable "plex_token" {
type = string
sensitive = true
description = "Plex API token used as a header in the synthetic monitoring check for plex.pez.sh"
}

View file

@ -8,6 +8,7 @@ locals {
nuremberg_a = hcloud_server.nuremberg-a.ipv4_address
nuremberg_aaaa = hcloud_server.nuremberg-a.ipv6_address
copenhagen = "83.94.248.182"
dns_ttl = 300
}
resource "hcloud_zone_rrset" "A_helsinki_a" {
@ -20,7 +21,7 @@ resource "hcloud_zone_rrset" "A_helsinki_a" {
zone = hcloud_zone.pezsh.name
name = each.value
type = "A"
ttl = 300
ttl = local.dns_ttl
records = [{ value = local.helsinki_a }]
}
@ -32,7 +33,7 @@ resource "hcloud_zone_rrset" "nuremberg_mail" {
zone = hcloud_zone.pezsh.name
name = "mail"
type = each.key
ttl = 300
ttl = local.dns_ttl
records = [{ value = each.value }]
}
@ -41,7 +42,7 @@ resource "hcloud_zone_rrset" "A_copenhagen" {
zone = hcloud_zone.pezsh.name
name = each.value
type = "A"
ttl = 300
ttl = local.dns_ttl
records = [{ value = local.copenhagen }]
}
@ -49,7 +50,7 @@ resource "hcloud_zone_rrset" "CNAME_public" {
zone = hcloud_zone.pezsh.name
name = "public"
type = "CNAME"
ttl = 300
ttl = local.dns_ttl
records = [{ value = "public.r2.dev." }]
}
@ -57,7 +58,7 @@ resource "hcloud_zone_rrset" "MX_root" {
zone = hcloud_zone.pezsh.name
name = "@"
type = "MX"
ttl = 300
ttl = local.dns_ttl
records = [
{ value = "10 mail.pez.sh." },
]
@ -67,7 +68,7 @@ resource "hcloud_zone_rrset" "TXT_dkim" {
zone = hcloud_zone.pezsh.name
name = "dkim._domainkey"
type = "TXT"
ttl = 300
ttl = local.dns_ttl
records = [{
value = "\"v=DKIM1;k=rsa;t=s;s=email;p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAmT/TGkPkfbjleqRYuQoI67/xvM0J5gGmdlzo2jO5qTABz5+nzOS+PefrXkeEZ0IZrpLPKqLyi7K469Ql+HG5wDFDxQRRG7lHJkWJ4tnZgjZWgeszFPhoME74lT6i+j3x29WyxhyzNg0f3NhSwttOe5knmS4zsOb+JK4jShoF9zZkOUCHAZ/vKvY\" \"tJdV+8qpmU8wfgyrzN1OWxjHIjzPP8iMD4g0iCfobbvSvWXHYBveCS7b/Nr3jw3E8twtEAUEGYNGd4h0wKNbNagYUsb5My8tMxQQwZf6imKHgCeYC7buH8TvaJHATReeea4Dzj9UzdPgwdbFLiMB/HXlN0GPhlQIDAQAB\""
}]
@ -77,7 +78,7 @@ resource "hcloud_zone_rrset" "TXT_dmarc" {
zone = hcloud_zone.pezsh.name
name = "_dmarc"
type = "TXT"
ttl = 300
ttl = local.dns_ttl
records = [{ value = "\"v=DMARC1; p=quarantine; rua=mailto:pez@pez.sh; adkim=r; aspf=r\"" }]
}
@ -85,6 +86,6 @@ resource "hcloud_zone_rrset" "TXT_spf" {
zone = hcloud_zone.pezsh.name
name = "@"
type = "TXT"
ttl = 300
ttl = local.dns_ttl
records = [{ value = "\"v=spf1 ip4:${local.nuremberg_a} ip6:${local.nuremberg_aaaa} -all\"" }]
}

View file

@ -0,0 +1,12 @@
output "server_ips" {
description = "Public IPv4 addresses of all managed servers"
value = {
nuremberg_a = hcloud_server.nuremberg-a.ipv4_address
helsinki_a = hcloud_server.helsinki-a.ipv4_address
}
}
output "dns_zone" {
description = "The managed DNS zone name"
value = hcloud_zone.pezsh.name
}

View file

@ -1,11 +1,7 @@
data "pagerduty_vendor" "prometheus" {
name = "Prometheus"
}
resource "pagerduty_service_integration" "grafana_cloud" {
name = "Grafana"
service = pagerduty_service.pez_solutions.id
vendor = data.pagerduty_vendor.prometheus.id
vendor = ""
}
output "pagerduty_integration_key" {