locals { env = module.envs.envs[var.env] project = local.env.project cloudflare = local.env.cloudflare workers = local.env.workers } module "envs" { source = "../envs" } # control creates every secret read here, and the values go in with gcloud secrets versions add data "google_secret_manager_secret_version" "token" { project = local.project secret = local.cloudflare.token_secret } provider "cloudflare" { # the provider refuses a token ending in a newline, and echo into gcloud adds one api_token = trimspace(data.google_secret_manager_secret_version.token.secret_data) } locals { secret_bindings = merge([ for w, c in local.workers : { for binding, id in try(c.secrets, {}) : "${w}/${binding}" => { worker = w, binding = binding, id = id } } ]...) domains = merge([ for w, c in local.workers : { for d in try(c.domains, []) : "${w}/${d}" => { worker = w, hostname = d } } ]...) routes = merge([ for w, c in local.workers : { for r in try(c.routes, []) : "${w}/${r.pattern}" => merge(r, { worker = w }) } ]...) workflows = merge([ for w, c in local.workers : { for binding, f in try(c.workflows, {}) : "${w}/${binding}" => merge(f, { worker = w, binding = binding }) } ]...) kv_namespaces = merge([ for w, c in local.workers : { for binding, title in try(c.kv, {}) : "${w}/${binding}" => { worker = w, binding = binding, title = title } } ]...) r2_buckets = merge([ for w, c in local.workers : { for binding, name in try(c.r2, {}) : "${w}/${binding}" => { worker = w, binding = binding, name = name } } ]...) # wrangler's default module rules, by extension module_types = { js = "application/javascript+module" mjs = "application/javascript+module" wasm = "application/wasm" txt = "text/plain" html = "text/plain" sql = "text/plain" } } data "google_secret_manager_secret_version" "binding" { for_each = local.secret_bindings project = local.project secret = each.value.id } data "google_secret_manager_secret_version" "key" { for_each = { for w, c in local.workers : w => c if try(c.google_identity, null) != null } project = local.project secret = "${each.key}-google-key" } resource "cloudflare_workers_kv_namespace" "this" { for_each = local.kv_namespaces account_id = local.cloudflare.account_id title = each.value.title } resource "cloudflare_r2_bucket" "this" { for_each = local.r2_buckets account_id = local.cloudflare.account_id name = each.value.name } resource "cloudflare_workers_script" "this" { for_each = local.workers account_id = local.cloudflare.account_id script_name = each.value.name compatibility_date = each.value.compatibility_date compatibility_flags = try(each.value.compatibility_flags, null) limits = try(each.value.limits, null) main_module = "index.js" # a missing bundle would read as "delete the worker", so this fails instead content_file = "${var.bundles[each.key]}/index.js" content_sha256 = filesha256("${var.bundles[each.key]}/index.js") # the ./ prefix matches the specifiers wrangler writes into index.js files = { for f in setsubtract(fileset(var.bundles[each.key], "**"), ["index.js"]) : "./${f}" => { content_file = "${var.bundles[each.key]}/${f}" content_type = lookup(local.module_types, reverse(split(".", f))[0], "application/octet-stream") } } bindings = concat( [for name, text in try(each.value.vars, {}) : { type = "plain_text", name = name, text = text }], [ for k, s in local.secret_bindings : { type = "secret_text" name = s.binding text = data.google_secret_manager_secret_version.binding[k].secret_data } if s.worker == each.key ], [ for k, v in local.kv_namespaces : { type = "kv_namespace" name = v.binding namespace_id = cloudflare_workers_kv_namespace.this[k].id } if v.worker == each.key ], [ for k, v in local.r2_buckets : { type = "r2_bucket" name = v.binding bucket_name = cloudflare_r2_bucket.this[k].name } if v.worker == each.key ], try(each.value.google_identity, null) == null ? [] : [{ type = "secret_text" name = each.value.google_identity.key_binding text = data.google_secret_manager_secret_version.key[each.key].secret_data }], [ for f in values(local.workflows) : { type = "workflow" name = f.binding workflow_name = f.name class_name = f.class_name } if f.worker == each.key ], try(each.value.bindings, []), ) # cloudflare's defaults for enabled = true, or every plan shows a change observability = { enabled = true head_sampling_rate = 1 logs = { enabled = true, head_sampling_rate = 1, invocation_logs = true, persist = true } traces = { enabled = false, head_sampling_rate = 1, persist = true } } } resource "cloudflare_workers_script_subdomain" "this" { for_each = local.workers account_id = local.cloudflare.account_id script_name = cloudflare_workers_script.this[each.key].script_name enabled = try(each.value.workers_dev, false) previews_enabled = false } resource "cloudflare_workers_custom_domain" "this" { for_each = local.domains account_id = local.cloudflare.account_id hostname = each.value.hostname service = cloudflare_workers_script.this[each.value.worker].script_name } data "cloudflare_zone" "route" { for_each = toset([for r in local.routes : r.zone]) filter = { name = each.value } } resource "cloudflare_workers_route" "this" { for_each = local.routes zone_id = data.cloudflare_zone.route[each.value.zone].id pattern = each.value.pattern script = cloudflare_workers_script.this[each.value.worker].script_name } resource "cloudflare_workflow" "this" { for_each = local.workflows account_id = local.cloudflare.account_id workflow_name = each.value.name class_name = each.value.class_name script_name = cloudflare_workers_script.this[each.value.worker].script_name schedules = [for cron in try(each.value.schedules, []) : { cron = cron }] } resource "cloudflare_workers_cron_trigger" "this" { for_each = { for w, c in local.workers : w => c if length(try(c.crons, [])) > 0 } account_id = local.cloudflare.account_id script_name = cloudflare_workers_script.this[each.key].script_name schedules = [for cron in each.value.crons : { cron = cron }] }