Pablo Alejandro Costesich's Blog

Invalidating caches after deploying

Posted by @pcostesi on 2021-07-22Time to read: 20 minutes

In the last blog post, we set up Cloudflare so we could have TLS on the cheap. I incorrectly stated that we didn't need to modify the build process: we need to tell Cloudflare that the old content should be purged, or that it shouldn't cache html files.

We need to set up an API token with limited access in Cloudflare. This allows us to keep our site updated through the builder without exposing our master key:

resource "google_cloudbuild_trigger" "site_trigger" {
provider = google-beta
name = "site-trigger"
project = var.project
github {
push {
branch = "master"
}
owner = "pcostesi"
name = "pcostesi.dev"
}
substitutions = {
"_CF_ZONE" = cloudflare_zone.main_site[0].id
"_CF_USER" = var.email_address
"_CF_API_TOKEN" = cloudflare_api_token.dns_tls_purge_cache[0].value
}
filename = "cloudbuild.yaml"
}
view raw build.tf hosted with ❤ by GitHub
data "cloudflare_api_token_permission_groups" "all" {}
resource "cloudflare_api_token" "dns_tls_purge_cache" {
name = "dns_tls_purge_cache"
policy {
permission_groups = [
data.cloudflare_api_token_permission_groups.all.permissions["Cache Purge"],
]
resources = {
"com.cloudflare.api.account.zone.${cloudflare_zone.main_site[0].id}" = "*"
}
}
count = var.use_cloudflare ? 1 : 0
}
view raw cache.tf hosted with ❤ by GitHub
steps:
- name: node:current
entrypoint: npm
args: ["install"]
- name: node:current
entrypoint: npx
args: ["browserslist@latest", "--update-db"]
- name: node:current
entrypoint: npm
args: ["run", "build"]
- name: gcr.io/cloud-builders/gsutil
args: ["-m", "rsync", "-r", "-c", "-d", "./public", "gs://pcostesi.dev"]
- name: gcr.io/cloud-builders/gcloud
entrypoint: "bash"
args:
- "-c"
- |
curl -X POST "https://api.cloudflare.com/client/v4/zones/${_CF_ZONE}/purge_cache" \
-H "X-Auth-Email: ${_CF_USER}" \
-H "Authorization: Bearer ${_CF_API_TOKEN}" \
-H "Content-Type: application/json" \
--data '{"purge_everything":true}'
view raw cloudbuild.yaml hosted with ❤ by GitHub
# Global IP, needed for the load balancer (maybe this should go to `routing.tf`)
resource "google_compute_global_address" "static_sites" {
provider = google
name = "static-sites"
count = var.use_cloudflare ? 0 : 1
}
# Managed DNS zone
data "google_dns_managed_zone" "main_site" {
provider = google
name = var.project
count = var.use_cloudflare ? 0 : 1
}
# Bind IP to the DNS
resource "google_dns_record_set" "static_site" {
provider = google
name = "${var.static_site}."
type = "A"
ttl = 300
managed_zone = data.google_dns_managed_zone.main_site[0].name
rrdatas = [google_compute_global_address.static_sites[0].address]
count = var.use_cloudflare ? 0 : 1
}
# Site verification
resource "google_dns_record_set" "static_site_txt" {
provider = google
name = "${var.static_site}."
type = "TXT"
ttl = 300
managed_zone = data.google_dns_managed_zone.main_site[0].name
rrdatas = ["google-site-verification=KNrjsg5IVfr7ivKz94SoZ06U7nTtfOsmuzZRncbXpRI"]
count = var.use_cloudflare ? 0 : 1
}
resource "cloudflare_zone" "main_site" {
zone = var.static_site
count = var.use_cloudflare ? 1 : 0
}
resource "cloudflare_zone_settings_override" "main_site" {
zone_id = cloudflare_zone.main_site[0].id
settings {
ssl = "strict"
always_use_https = "on"
automatic_https_rewrites = "on"
}
count = var.use_cloudflare ? 1 : 0
}
resource "cloudflare_record" "static_site" {
zone_id = cloudflare_zone.main_site[0].id
name = "${var.static_site}."
value = "c.storage.googleapis.com"
type = "CNAME"
ttl = 1
proxied = true
count = var.use_cloudflare ? 1 : 0
}
resource "cloudflare_record" "static_site_txt" {
zone_id = cloudflare_zone.main_site[0].id
name = "${var.static_site}."
type = "TXT"
ttl = 300
value = "google-site-verification=${var.google_site_verification}"
count = var.use_cloudflare ? 1 : 0
}
view raw dns.tf hosted with ❤ by GitHub

You can check the token permissions here: Cloudflare API docs.

Next, we need to tell the build step the content of the key:

resource "google_cloudbuild_trigger" "site_trigger" {
provider = google-beta
name = "site-trigger"
project = var.project
github {
push {
branch = "master"
}
owner = "pcostesi"
name = "pcostesi.dev"
}
substitutions = {
"_CF_ZONE" = cloudflare_zone.main_site[0].id
"_CF_USER" = var.email_address
"_CF_API_TOKEN" = cloudflare_api_token.dns_tls_purge_cache[0].value
}
filename = "cloudbuild.yaml"
}
view raw build.tf hosted with ❤ by GitHub
data "cloudflare_api_token_permission_groups" "all" {}
resource "cloudflare_api_token" "dns_tls_purge_cache" {
name = "dns_tls_purge_cache"
policy {
permission_groups = [
data.cloudflare_api_token_permission_groups.all.permissions["Cache Purge"],
]
resources = {
"com.cloudflare.api.account.zone.${cloudflare_zone.main_site[0].id}" = "*"
}
}
count = var.use_cloudflare ? 1 : 0
}
view raw cache.tf hosted with ❤ by GitHub
steps:
- name: node:current
entrypoint: npm
args: ["install"]
- name: node:current
entrypoint: npx
args: ["browserslist@latest", "--update-db"]
- name: node:current
entrypoint: npm
args: ["run", "build"]
- name: gcr.io/cloud-builders/gsutil
args: ["-m", "rsync", "-r", "-c", "-d", "./public", "gs://pcostesi.dev"]
- name: gcr.io/cloud-builders/gcloud
entrypoint: "bash"
args:
- "-c"
- |
curl -X POST "https://api.cloudflare.com/client/v4/zones/${_CF_ZONE}/purge_cache" \
-H "X-Auth-Email: ${_CF_USER}" \
-H "Authorization: Bearer ${_CF_API_TOKEN}" \
-H "Content-Type: application/json" \
--data '{"purge_everything":true}'
view raw cloudbuild.yaml hosted with ❤ by GitHub
# Global IP, needed for the load balancer (maybe this should go to `routing.tf`)
resource "google_compute_global_address" "static_sites" {
provider = google
name = "static-sites"
count = var.use_cloudflare ? 0 : 1
}
# Managed DNS zone
data "google_dns_managed_zone" "main_site" {
provider = google
name = var.project
count = var.use_cloudflare ? 0 : 1
}
# Bind IP to the DNS
resource "google_dns_record_set" "static_site" {
provider = google
name = "${var.static_site}."
type = "A"
ttl = 300
managed_zone = data.google_dns_managed_zone.main_site[0].name
rrdatas = [google_compute_global_address.static_sites[0].address]
count = var.use_cloudflare ? 0 : 1
}
# Site verification
resource "google_dns_record_set" "static_site_txt" {
provider = google
name = "${var.static_site}."
type = "TXT"
ttl = 300
managed_zone = data.google_dns_managed_zone.main_site[0].name
rrdatas = ["google-site-verification=KNrjsg5IVfr7ivKz94SoZ06U7nTtfOsmuzZRncbXpRI"]
count = var.use_cloudflare ? 0 : 1
}
resource "cloudflare_zone" "main_site" {
zone = var.static_site
count = var.use_cloudflare ? 1 : 0
}
resource "cloudflare_zone_settings_override" "main_site" {
zone_id = cloudflare_zone.main_site[0].id
settings {
ssl = "strict"
always_use_https = "on"
automatic_https_rewrites = "on"
}
count = var.use_cloudflare ? 1 : 0
}
resource "cloudflare_record" "static_site" {
zone_id = cloudflare_zone.main_site[0].id
name = "${var.static_site}."
value = "c.storage.googleapis.com"
type = "CNAME"
ttl = 1
proxied = true
count = var.use_cloudflare ? 1 : 0
}
resource "cloudflare_record" "static_site_txt" {
zone_id = cloudflare_zone.main_site[0].id
name = "${var.static_site}."
type = "TXT"
ttl = 300
value = "google-site-verification=${var.google_site_verification}"
count = var.use_cloudflare ? 1 : 0
}
view raw dns.tf hosted with ❤ by GitHub

Finally, we need to add the purge step in the cloudbuild.yaml in our blog:

resource "google_cloudbuild_trigger" "site_trigger" {
provider = google-beta
name = "site-trigger"
project = var.project
github {
push {
branch = "master"
}
owner = "pcostesi"
name = "pcostesi.dev"
}
substitutions = {
"_CF_ZONE" = cloudflare_zone.main_site[0].id
"_CF_USER" = var.email_address
"_CF_API_TOKEN" = cloudflare_api_token.dns_tls_purge_cache[0].value
}
filename = "cloudbuild.yaml"
}
view raw build.tf hosted with ❤ by GitHub
data "cloudflare_api_token_permission_groups" "all" {}
resource "cloudflare_api_token" "dns_tls_purge_cache" {
name = "dns_tls_purge_cache"
policy {
permission_groups = [
data.cloudflare_api_token_permission_groups.all.permissions["Cache Purge"],
]
resources = {
"com.cloudflare.api.account.zone.${cloudflare_zone.main_site[0].id}" = "*"
}
}
count = var.use_cloudflare ? 1 : 0
}
view raw cache.tf hosted with ❤ by GitHub
steps:
- name: node:current
entrypoint: npm
args: ["install"]
- name: node:current
entrypoint: npx
args: ["browserslist@latest", "--update-db"]
- name: node:current
entrypoint: npm
args: ["run", "build"]
- name: gcr.io/cloud-builders/gsutil
args: ["-m", "rsync", "-r", "-c", "-d", "./public", "gs://pcostesi.dev"]
- name: gcr.io/cloud-builders/gcloud
entrypoint: "bash"
args:
- "-c"
- |
curl -X POST "https://api.cloudflare.com/client/v4/zones/${_CF_ZONE}/purge_cache" \
-H "X-Auth-Email: ${_CF_USER}" \
-H "Authorization: Bearer ${_CF_API_TOKEN}" \
-H "Content-Type: application/json" \
--data '{"purge_everything":true}'
view raw cloudbuild.yaml hosted with ❤ by GitHub
# Global IP, needed for the load balancer (maybe this should go to `routing.tf`)
resource "google_compute_global_address" "static_sites" {
provider = google
name = "static-sites"
count = var.use_cloudflare ? 0 : 1
}
# Managed DNS zone
data "google_dns_managed_zone" "main_site" {
provider = google
name = var.project
count = var.use_cloudflare ? 0 : 1
}
# Bind IP to the DNS
resource "google_dns_record_set" "static_site" {
provider = google
name = "${var.static_site}."
type = "A"
ttl = 300
managed_zone = data.google_dns_managed_zone.main_site[0].name
rrdatas = [google_compute_global_address.static_sites[0].address]
count = var.use_cloudflare ? 0 : 1
}
# Site verification
resource "google_dns_record_set" "static_site_txt" {
provider = google
name = "${var.static_site}."
type = "TXT"
ttl = 300
managed_zone = data.google_dns_managed_zone.main_site[0].name
rrdatas = ["google-site-verification=KNrjsg5IVfr7ivKz94SoZ06U7nTtfOsmuzZRncbXpRI"]
count = var.use_cloudflare ? 0 : 1
}
resource "cloudflare_zone" "main_site" {
zone = var.static_site
count = var.use_cloudflare ? 1 : 0
}
resource "cloudflare_zone_settings_override" "main_site" {
zone_id = cloudflare_zone.main_site[0].id
settings {
ssl = "strict"
always_use_https = "on"
automatic_https_rewrites = "on"
}
count = var.use_cloudflare ? 1 : 0
}
resource "cloudflare_record" "static_site" {
zone_id = cloudflare_zone.main_site[0].id
name = "${var.static_site}."
value = "c.storage.googleapis.com"
type = "CNAME"
ttl = 1
proxied = true
count = var.use_cloudflare ? 1 : 0
}
resource "cloudflare_record" "static_site_txt" {
zone_id = cloudflare_zone.main_site[0].id
name = "${var.static_site}."
type = "TXT"
ttl = 300
value = "google-site-verification=${var.google_site_verification}"
count = var.use_cloudflare ? 1 : 0
}
view raw dns.tf hosted with ❤ by GitHub