fix(iac): pin cloudflare provider + lockfile, trust homelab CA in gitea provider #12

Merged
arcodange merged 1 commits from arcodange/iac-provider-fixes into main 2026-06-24 13:03:18 +02:00
Owner

Context

After the runner-CA fix (#11), the iac workflow runs far enough to reach terraform apply (run #27), which surfaced two provider-level failures. This PR fixes both. It complements #11 (merge both); no overlapping lines.

1. Cloudflare provider drift

cloudflare/cloudflare was constrained to ~> 5 with no committed .terraform.lock.hcl (the old .gitignore rule .terraform.* was hiding it), so every CI init pulled the latest — now v5.21.1 — where cloudflare_account_token.policies[].resources is a JSON string, not an HCL map:

Error: Incorrect attribute value type ... attribute "resources": string required

Fixes:

  • Pin ~> 5.21 and commit a multi-platform lock file (linux_arm64 for the runner + darwin_arm64 for local) — stops the drift.
  • jsonencode(...) the module's policy resources (correct shape for ≥5.20; verified against the provider schema).
  • Bind the cloudflare_token module to cloudflare/cloudflare via its own required_providers — it was defaulting to hashicorp/cloudflare, pulling a redundant second provider.
  • .gitignore: .terraform / .terraform.*.terraform/ + *.tfstate*, so the lock file is tracked.

2. Gitea provider TLS (x509)

Get "https://gitea.arcodange.lab/api/v1/version": x509: certificate signed by unknown authority

The gitea provider runs inside the dflook/terraform-apply container, which doesn't trust the homelab CA (only the ubuntu-latest-ca runner does; the vault provider survives because it honors VAULT_CACERT). Fix: feed the gitea provider the CA via its cacert_file argument, sourced from TF_VAR_gitea_cacert_file → the homelab.pem the workflow already writes to the workspace.

Validation

Done locally, no prod calls:

  • tofu init resolves to v5.21.1 + generates the lock file with both platforms.
  • tofu validateSuccess.
  • tofu providers schema confirmed policies[].resources is string and the gitea provider exposes cacert_file.
  • Lock file no longer gitignored; hashicorp/cloudflare removed.

I could not exercise a real plan/apply (needs Vault JWT + prod credentials + the OAuth handoff).

Out of scope (need a live run / operator)

  • OVH consumer-key scope — the regenerated key authenticates but lacks access rules (403 "This call has not been granted"); recreate at auth.eu.ovhcloud.com/api/createToken with GET/POST/PUT/DELETE on /me/* and /iam/*.
  • R2 bucket "not found" on refresh — the arcodange-tf bucket exists (EU jurisdiction, confirmed via API) but v5.21.1 reports it missing on refresh. This is a state/read reconcile (likely a one-time tofu import), not fixable offline — verify on the next live run and don't let auto_approve recreate it.
  • postgres/iac uses exact version pins (low drift risk); a lock file there is a possible follow-up.

🤖 Generated with Claude Code

## Context After the runner-CA fix (#11), the `iac` workflow runs far enough to reach `terraform apply` ([run #27](https://gitea.arcodange.lab/arcodange-org/factory/actions/runs/27)), which surfaced two provider-level failures. This PR fixes both. It **complements #11** (merge both); no overlapping lines. ## 1. Cloudflare provider drift `cloudflare/cloudflare` was constrained to `~> 5` with **no committed `.terraform.lock.hcl`** (the old `.gitignore` rule `.terraform.*` was hiding it), so every CI `init` pulled the latest — now **v5.21.1** — where `cloudflare_account_token.policies[].resources` is a **JSON string**, not an HCL map: ``` Error: Incorrect attribute value type ... attribute "resources": string required ``` Fixes: - **Pin** `~> 5.21` and **commit a multi-platform lock file** (`linux_arm64` for the runner + `darwin_arm64` for local) — stops the drift. - **`jsonencode(...)`** the module's policy `resources` (correct shape for ≥5.20; verified against the provider schema). - **Bind the `cloudflare_token` module to `cloudflare/cloudflare`** via its own `required_providers` — it was defaulting to `hashicorp/cloudflare`, pulling a redundant second provider. - **`.gitignore`**: `.terraform` / `.terraform.*` → `.terraform/` + `*.tfstate*`, so the lock file is tracked. ## 2. Gitea provider TLS (x509) ``` Get "https://gitea.arcodange.lab/api/v1/version": x509: certificate signed by unknown authority ``` The gitea provider runs inside the `dflook/terraform-apply` **container**, which doesn't trust the homelab CA (only the `ubuntu-latest-ca` runner does; the vault provider survives because it honors `VAULT_CACERT`). Fix: feed the gitea provider the CA via its `cacert_file` argument, sourced from `TF_VAR_gitea_cacert_file` → the `homelab.pem` the workflow already writes to the workspace. ## Validation Done **locally, no prod calls**: - `tofu init` resolves to v5.21.1 + generates the lock file with both platforms. - `tofu validate` → **Success**. - `tofu providers schema` confirmed `policies[].resources` is `string` and the gitea provider exposes `cacert_file`. - Lock file no longer gitignored; `hashicorp/cloudflare` removed. I could not exercise a real `plan`/`apply` (needs Vault JWT + prod credentials + the OAuth handoff). ## Out of scope (need a live run / operator) - **OVH consumer-key scope** — the regenerated key authenticates but lacks access rules (`403 "This call has not been granted"`); recreate at `auth.eu.ovhcloud.com/api/createToken` with `GET/POST/PUT/DELETE` on `/me/*` and `/iam/*`. - **R2 bucket "not found" on refresh** — the `arcodange-tf` bucket exists (EU jurisdiction, confirmed via API) but v5.21.1 reports it missing on refresh. This is a state/read reconcile (likely a one-time `tofu import`), not fixable offline — verify on the next live run and **don't let `auto_approve` recreate it**. - `postgres/iac` uses exact version pins (low drift risk); a lock file there is a possible follow-up. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
arcodange added 1 commit 2026-06-24 12:57:13 +02:00
With the runner CA fix (#11) the iac workflow now runs far enough to apply,
which exposed two provider problems:

cloudflare drift — `cloudflare/cloudflare` floated on `~> 5` with no committed
lock file, so CI pulled v5.21.1 where `cloudflare_account_token.policies[].resources`
is a JSON string, not a map ("Incorrect attribute value type"). Fix:
- pin to `~> 5.21` and commit a multi-platform `.terraform.lock.hcl`
  (linux_arm64 for the runner + darwin_arm64 for local);
- `jsonencode(...)` the module's policy resources;
- bind the cloudflare_token module to `cloudflare/cloudflare` explicitly (it was
  defaulting to `hashicorp/cloudflare`, pulling a redundant provider);
- stop `.gitignore` from hiding the lock file (the old `.terraform.*` rule did).

gitea provider TLS — it runs inside the dflook/terraform-apply container, which
doesn't trust the homelab CA (only the ubuntu-latest-ca runner does), so it
failed `x509: certificate signed by unknown authority` reaching
gitea.arcodange.lab. Fix: feed it the homelab CA via the provider's `cacert_file`
(TF_VAR_gitea_cacert_file -> the homelab.pem the workflow already materializes).

Validated locally with `tofu validate` + provider-schema inspection (no prod
calls). Complements #11. Out of scope (need a live run / operator): the OVH
consumer-key scope, and the R2 bucket "not found" on refresh (a state reconcile).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
arcodange merged commit fc28c52b85 into main 2026-06-24 13:03:18 +02:00
Sign in to join this conversation.
No Reviewers
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: arcodange-org/factory#12