1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
|
# One Cloudflare Pages project + custom domain + DNS record per language,
# matching blog/hugo.toml's multihost [languages.*] baseURLs: each language
# is a fully separate site (public/<lang>/ from the Hugo build) deployed to
# its own subdomain, with English at the zone apex.
#
# The language codes themselves come from ../languages.txt, the same file
# the Makefile reads for its deploy fan-out, so the two never drift apart.
# blog/hugo.toml's [languages.*] blocks can't read that file too (Tofu has
# no TOML parser to return the favor) - keep that one in sync by hand.
locals {
language_codes = split(" ", trimspace(file("${path.module}/../languages.txt")))
languages = {
for code in local.language_codes : code => {
hostname = code == "en" ? var.zone_name : "${code}.${var.zone_name}"
project_name = "mykiwi-blog-${code}"
}
}
}
data "cloudflare_zone" "this" {
name = var.zone_name
}
# Direct-upload projects (no `source` block, i.e. no git integration) -
# deploys happen out-of-band, e.g. `wrangler pages deploy public/en
# --project-name=mykiwi-blog-en` from CI, since this repo lives on
# tangled.sh rather than GitHub.
resource "cloudflare_pages_project" "site" {
for_each = local.languages
account_id = var.cloudflare_account_id
name = each.value.project_name
production_branch = "main"
}
resource "cloudflare_pages_domain" "site" {
for_each = local.languages
account_id = var.cloudflare_account_id
project_name = cloudflare_pages_project.site[each.key].name
domain = each.value.hostname
}
resource "cloudflare_record" "site" {
for_each = local.languages
zone_id = data.cloudflare_zone.this.id
name = each.key == "en" ? "@" : each.key
type = "CNAME"
content = cloudflare_pages_project.site[each.key].subdomain
proxied = true
}
# www.mykiwi.blog -> mykiwi.blog (English apex), not its own language - just
# the one common typo/habit redirect, not a 5th site.
resource "cloudflare_record" "www" {
zone_id = data.cloudflare_zone.this.id
name = "www"
type = "CNAME"
content = var.zone_name
proxied = true
}
# Page Rules would also do this but its Edit permission only shows up in
# the API token builder for account Super Administrators - Single Redirects
# (this resource) works with a normal scoped token (Zone:Dynamic Redirect:Edit).
resource "cloudflare_ruleset" "www_redirect" {
zone_id = data.cloudflare_zone.this.id
name = "www to apex redirect"
description = "www.${var.zone_name} -> ${var.zone_name}, path preserved"
kind = "zone"
phase = "http_request_dynamic_redirect"
rules {
ref = "www_redirect"
description = "Redirect www to apex"
expression = "(http.host eq \"www.${var.zone_name}\")"
action = "redirect"
action_parameters {
from_value {
status_code = 301
target_url {
expression = "concat(\"https://${var.zone_name}\", http.request.uri.path)"
}
preserve_query_string = true
}
}
}
}
|