When to use
Before running terraform apply on the iac-access/foundation/ layer — whether for a first-time environment bootstrap, a routine change, or after a terraform taint cloudflare_turnstile_widget.access.
The foundation layer manages Cloudflare Turnstile resources in addition to GCP resources. The Cloudflare API token must carry the right scopes or the apply fails silently with a bare 403.
Preconditions
- Access to the
iac-accessrepository and the target environment's tfvars. -
CLOUDFLARE_API_TOKENwith both of the following scopes:Account > Turnstile > EditAccount > Account Settings > Read
-
TF_VAR_iac_projectset (or injected by Terraflow viaTF_VAR_*). - GCP credentials with the permissions normally required by the
foundationlayer (Cloud SQL admin, Secret Manager admin, IAM admin, etc.).
Verifying CF API token scopes
- Go to Cloudflare Dashboard → My Profile → API Tokens.
- Find the token used for Terraform CI (or your local token).
- Click Edit and confirm both scopes are present under the correct account.
If the token is managed via GCP Secret Manager (CI flow), the token value is in terraform-cf-api-token secret. Update the secret version after editing the token in the CF Dashboard.
Procedure
Via Terraflow (standard — CI)
# Push your changes and open a PR against iac-access/main
# CI runs terraform plan automatically for the foundation layer.
# Review the plan comment posted by eigenoid-terraflow-bot.
# If the plan is correct, comment on the PR:
/terraflow apply foundation/
Terraflow sets CLOUDFLARE_API_TOKEN from the CF_API_TOKEN secret configured in the GitHub environment. Ensure the secret value is up to date if the token was recently rotated.
Local apply (break-glass)
export CLOUDFLARE_API_TOKEN="<token with Turnstile+Edit and Account Settings+Read>"
export TF_VAR_iac_project="eigenoid-dev" # or qa/prd
cd iac-access/foundation
terraform init
terraform plan -var-file=tfvars/dev.tfvars
terraform apply -var-file=tfvars/dev.tfvars
After apply — copy sitekey to Workers Builds
The Turnstile sitekey is not automatically propagated to the Cloudflare Worker build environment. After each apply:
# Get the sitekey
terraform -chdir=foundation output turnstile_sitekey
Then:
- Go to CF Dashboard → Workers & Pages →
app-access-public-{env}. - Navigate to Settings → Builds → Build variables.
- Set or update
PUBLIC_TURNSTILE_SITE_KEYto the value fromterraform output. - Trigger a new build (push a commit or click Trigger deploy).
This manual step is required because the Cloudflare provider v5.19.0 does not expose Workers Builds variables as a Terraform resource. Tracked in ADR-0018.
Verification
# Confirm the Turnstile secret key version exists in Secret Manager
gcloud secrets versions list access-turnstile-secret-key \
--project=eigenoid-{env} \
--format="table(name, state, createTime)"
# Expect: at least one version with state ENABLED
# Confirm the public portal picks up the new sitekey
# (after Workers Builds step above):
curl -s https://access-{env}.eigenoid.com | grep turnstile
# Expect: the sitekey value appearing in the page source
Troubleshooting
403 on cloudflare_turnstile_widget.access creation
Symptom: terraform apply fails with a 403 error, no resource name in the message.
Cause: CLOUDFLARE_API_TOKEN is missing Account > Turnstile > Edit or Account > Account Settings > Read.
Fix: Update the CF API token to include both scopes. For CI, update the CF_API_TOKEN secret in the GitHub environment (iac-access → Settings → Environments → dev/qa/prd).
Secret version already exists — apply is a no-op on the secret
Symptom: terraform plan shows google_secret_manager_secret_version.turnstile_secret_key will be replaced (not just updated).
Cause: A previous manual secret version exists. Terraform will create a new version (Secret Manager keeps all versions).
Fix: After apply, disable or destroy the old manual version if needed to keep the secret clean:
gcloud secrets versions list access-turnstile-secret-key --project=eigenoid-{env}
# Identify old manual versions and disable them:
gcloud secrets versions disable VERSION_ID \
--secret=access-turnstile-secret-key \
--project=eigenoid-{env}
service/ wave fails — "no version found for secret access-turnstile-secret-key"
Cause: foundation/ was not applied before service/, or the apply failed partway through.
Fix: Ensure foundation/ completed successfully (check CI logs or terraform output turnstile_sitekey), then re-apply service/.
Rollback
The cloudflare_turnstile_widget resource cannot be partially rolled back — a recreated widget issues a new sitekey/secret pair. If a widget is accidentally destroyed:
- Run
terraform apply foundation/again — this recreates the widget and repopulates the secret automatically. - Copy the new
turnstile_sitekeyto the Workers Builds env var (see After apply). - Existing challenge tokens issued against the old widget are invalidated — users will need to complete a new Turnstile challenge.
Escalation
Escalate to @shoootyou if the apply fails with an error not covered above.