Communitygithub.com

dortort/skills

Wiederverwendbare KI-Agenten-Fähigkeiten für KI-Tools

Was ist skills?

Diese Fähigkeit beschreibt ein robustes Brückenmuster (Bridge Pattern) zur Integration von Cloudflare-Terraform-Infrastruktur mit Wrangler-Anwendungsdeployments. Sie adressiert das Kernproblem, dass Terraform Ressourcen-IDs (für KV-Namespaces, D1-Datenbanken, R2-Buckets, Queues) erstellt, die Wrangler für Bindungen benötigt. Die Lösung umfasst ein Node.js-Bridge-Skript, das Terraform-Ausgaben liest und eine Wrangler-Konfigurationsdatei (wrangler.jsonc) mit Platzhalter-Token generiert. Das Muster unterstützt CI/CD-Integration, lokale Entwicklung und Multi-Umgebungsszenarien (Staging/Produktion) und bietet eine Alternative für Teams, die Terraform als alleinige Quelle der Wahrheit bevorzugen. Es betont Wiederverwendbarkeit, Typsicherheit durch `wrangler types --check` und klare Trennung der Verantwortlichkeiten zwischen Infrastruktur- und Anwendungsverwaltung.

Funktioniert mit~Claude Code~Codex CLI~Cursor
npx skills add dortort/skills

In Ihrer bevorzugten KI fragen

Öffnet einen neuen Chat, in dem dieser Agent-Skill bereits geladen ist.

Dokumentation

Cloudflare IaC: Terraform + Wrangler

Das Kernproblem

Cloudflares Tooling ist in zwei Welten geteilt:

  • Terraform verwaltet die Infrastruktur: Zonen, DNS-Einträge, Zugriffsrichtlinien, Firewall-Regeln, Kontoebenen-Einstellungen und die Existenz von Ressourcen wie KV-Namespaces, D1-Datenbanken, R2-Buckets und Queues.
  • Wrangler verwaltet die Anwendungsbereitstellung: Worker-Code, Pages-Projekte, Bindungen zu diesen Ressourcen und die lokale Entwicklung.

Die Herausforderung besteht darin, dass Wrangler Ressourcen-IDs benötigt, die Terraform erstellt. Eine KV-Namespace-ID, eine D1-Datenbank-ID, ein Queue-Name – das sind Ausgaben von terraform apply, die in wrangler.toml (oder wrangler.jsonc) fließen müssen, bevor wrangler deploy funktionieren kann.

Diese Fähigkeit kodifiziert ein DRY-, automatisierbares Muster zur Überbrückung dieser Lücke.

Aufteilung der Verantwortlichkeiten

Terraform besitzt

  • Zonen und DNS-Einträge
  • KV-Namespaces (cloudflare_workers_kv_namespace)
  • D1-Datenbanken (cloudflare_d1_database)
  • R2-Buckets (cloudflare_r2_bucket)
  • Queues (cloudflare_queue)
  • Zugriffsrichtlinien, WAF-Regeln, Page-Regeln
  • Kontoebenen-Einstellungen
  • Jede Ressource, die vor der Bereitstellung des Anwendungscodes existieren sollte

Wrangler besitzt

  • Worker-Quellcode und Bündelung
  • Pages-Projektbereitstellung
  • Bindungskonfiguration (Verbindung von Workern mit den von Terraform erstellten Ressourcen)
  • Lokale Entwicklung (wrangler dev)
  • Secrets (via wrangler secret put)
  • Bereitstellung (wrangler deploy)

Die Grauzone

Einige Ressourcen können von beiden Tools verwaltet werden. Der Leitgedanke: Wenn mehrere Worker oder Projekte eine Ressource teilen, besitzt Terraform sie. Wenn sie auf einen einzelnen Worker beschränkt ist, kann Wrangler sie besitzen. Im Zweifelsfall bevorzugen Sie Terraform – es bietet Zustandsverfolgung, Drifterkennung und eine einzige Quelle der Wahrheit für die Infrastruktur.

Die Auto-Provisioning-Funktion von Wrangler (die KV-/R2-/D1-Ressourcen beim ersten Deploy erstellt) ist für Prototyping praktisch, sollte aber nicht in Produktions-Pipelines verwendet werden. Sie erstellt Ressourcen außerhalb des Terraform-Zustands, was zu Drift und Ressourcen führt, die nicht systematisch verwaltet oder abgebaut werden können.

Das Brückenmuster

Übersicht

terraform apply → terraform output -json → Brücken-Skript → wrangler.jsonc → wrangler deploy

Das Brücken-Skript ist ein Node.js-Skript im Projekt, das über package.json aufgerufen wird. Es liest Terraform-Ausgaben und schreibt sie in die Wrangler-Konfigurationsdatei. Dasselbe Skript wird lokal und in CI/CD ausgeführt – keine Duplizierung.

Schritt 1: Terraform-Ausgaben

Definieren Sie Ausgaben für jede Ressourcen-ID, die Wrangler benötigt:

# outputs.tf

output "kv_namespace_id" {
  value = cloudflare_workers_kv_namespace.my_kv.id
}

output "d1_database_id" {
  value = cloudflare_d1_database.my_db.id
}

output "r2_bucket_name" {
  value = cloudflare_r2_bucket.my_bucket.name
}

output "account_id" {
  value = var.cloudflare_account_id
}

output "zone_id" {
  value = cloudflare_zone.my_zone.id
}

Schritt 2: Das Brücken-Skript

Erstellen Sie scripts/sync-wrangler-config.mjs – ein Node.js-Skript, das Terraform-Ausgaben liest und die Wrangler-Konfiguration generiert (oder patcht):

#!/usr/bin/env node

// scripts/sync-wrangler-config.mjs
//
// Liest Terraform-Ausgaben und schreibt wrangler.jsonc mit den korrekten Ressourcen-IDs.
// Lokal oder in CI ausführen – gleicher Befehl, gleiches Ergebnis.

import { execSync } from "node:child_process";
import { readFileSync, writeFileSync, existsSync } from "node:fs";
import { join, dirname } from "node:path";
import { fileURLToPath } from "node:url";

const __dirname = dirname(fileURLToPath(import.meta.url));
const ROOT = join(__dirname, "..");

// --- Konfiguration ---
// Passen Sie diese Pfade an Ihr Projektlayout an.
const TERRAFORM_DIR = join(ROOT, "terraform");
const WRANGLER_CONFIG_PATH = join(ROOT, "wrangler.jsonc");
const TEMPLATE_PATH = join(ROOT, "wrangler.template.jsonc");

// --- Terraform-Ausgaben lesen ---
function getTerraformOutputs() {
  try {
    const raw = execSync("terraform output -json", {
      cwd: TERRAFORM_DIR,
      encoding: "utf-8",
      stdio: ["pipe", "pipe", "pipe"],
    });
    const outputs = JSON.parse(raw);
    // terraform output -json kapselt jeden Wert in { value, type, sensitive }
    const flat = {};
    for (const [key, entry] of Object.entries(outputs)) {
      flat[key] = entry.value;
    }
    return flat;
  } catch (err) {
    console.error("Fehler beim Lesen der Terraform-Ausgaben. Haben Sie 'terraform apply' ausgeführt?");
    console.error(err.message);
    process.exit(1);
  }
}

// --- Wrangler-Konfiguration generieren ---
function generateConfig(outputs) {
  if (!existsSync(TEMPLATE_PATH)) {
    console.error(`Vorlage nicht gefunden: ${TEMPLATE_PATH}`);
    console.error("Erstellen Sie eine wrangler.template.jsonc mit __PLACEHOLDER__-Token.");
    process.exit(1);
  }

  let template = readFileSync(TEMPLATE_PATH, "utf-8");

  // Ersetzen Sie __PLACEHOLDER__-Token durch Terraform-Ausgabewerte.
  // Token-Format: __UPPER_SNAKE_CASE__ zugeordnet zu Terraform-Ausgabenamen.
  for (const [key, value] of Object.entries(outputs)) {
    const token = `__${key.toUpperCase()}__`;
    template = template.replaceAll(token, String(value));
  }

  // Warnen bei nicht ersetzten Token
  const remaining = template.match(/__[A-Z_]+__/g);
  if (remaining) {
    console.warn("Warnung: Nicht ersetzte Token in der Vorlage:", [...new Set(remaining)]);
  }

  writeFileSync(WRANGLER_CONFIG_PATH, template, "utf-8");
  console.log(`Geschrieben: ${WRANGLER_CONFIG_PATH}`);
}

// --- Hauptprogramm ---
const outputs = getTerraformOutputs();
generateConfig(outputs);

Schritt 3: Die Vorlage

Erstellen Sie wrangler.template.jsonc – eine Wrangler-Konfiguration mit Platzhalter-Token, wo die Terraform-Ausgaben hingehören:

{
  // Generiert aus wrangler.template.jsonc von scripts/sync-wrangler-config.mjs
  // Nicht direkt bearbeiten – stattdessen die Vorlage bearbeiten.
  "name": "my-worker",
  "main": "src/index.ts",
  "compatibility_date": "2025-01-01",
  "account_id": "__ACCOUNT_ID__",

  "kv_namespaces": [
    {
      "binding": "MY_KV",
      "id": "__KV_NAMESPACE_ID__"
    }
  ],

  "d1_databases": [
    {
      "binding": "MY_DB",
      "database_id": "__D1_DATABASE_ID__",
      "database_name": "my-db"
    }
  ],

  "r2_buckets": [
    {
      "binding": "MY_BUCKET",
      "bucket_name": "__R2_BUCKET_NAME__"
    }
  ]
}

Schritt 4: package.json-Skripte

{
  "scripts": {
    "sync": "node scripts/sync-wrangler-config.mjs",
    "predeploy": "npm run sync",
    "deploy": "wrangler deploy",
    "dev": "npm run sync && wrangler dev"
  }
}

npm run deploy führt automatisch zuerst sync aus (über den predeploy-Hook). Derselbe npm run deploy-Befehl funktioniert lokal und in CI/CD – DRY.

Schritt 5: .gitignore

# Vom Sync-Skript generiert – nicht committen
wrangler.jsonc

# Terraform
terraform/.terraform/
terraform/*.tfstate*

Die generierte wrangler.jsonc sollte nicht committet werden. Die Vorlage und das Sync-Skript sind die Quelle der Wahrheit.

CI/CD-Integration

Die CI/CD-Pipeline führt genau dieselben Befehle aus, die ein Entwickler lokal ausführt. Der einzige Unterschied ist, wie der Terraform-Zustand und die Anmeldeinformationen bereitgestellt werden.

Generische Pipeline-Struktur

# Pseudocode – an die Syntax Ihrer CI/CD-Plattform anpassen

steps:
  # 1. Terraform wendet Infrastrukturänderungen an
  - name: terraform
    run: |
      cd terraform
      terraform init
      terraform apply -auto-approve

  # 2. Brücken-Skript synchronisiert Terraform-Ausgaben in die Wrangler-Konfiguration
  # 3. Wrangler stellt die Anwendung bereit
  - name: deploy-worker
    run: npm run deploy
    env:
      CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}

Das war's. npm run deploy ruft predeploy → sync → wrangler deploy auf. Gleich wie lokal.

Anmeldeinformationen

  • Terraform: benötigt ein Cloudflare-API-Token mit Berechtigung zur Verwaltung von Infrastrukturressourcen. Bereitstellung über die Umgebungsvariable CLOUDFLARE_API_TOKEN oder in der Provider-Konfiguration.
  • Wrangler: benötigt ein Cloudflare-API-Token mit Berechtigung zum Bereitstellen von Workern. Oft das gleiche Token, kann aber für das Prinzip der geringsten Rechte getrennt werden.
  • Terraform-Zustand: Verwenden Sie ein entferntes Backend (S3, GCS, Terraform Cloud usw.), damit CI und lokal denselben Zustand teilen.

Umgebungs-Promotion

Für Multi-Umgebungs-Setups (Staging, Produktion) verwenden Sie Terraform-Workspaces oder separate Zustandsdateien und Wrangler-Umgebungen:

// In wrangler.template.jsonc
{
  "name": "my-worker",
  "env": {
    "staging": {
      "kv_namespaces": [{ "binding": "MY_KV", "id": "__STAGING_KV_NAMESPACE_ID__" }]
    },
    "production": {
      "kv_namespaces": [{ "binding": "MY_KV", "id": "__PRODUCTION_KV_NAMESPACE_ID__" }]
    }
  }
}

Das Sync-Skript verarbeitet dies natürlich – fügen Sie einfach die entsprechenden Terraform-Ausgaben hinzu.

Alternative: Von Terraform verwaltete Bindungen

Für Teams, die Terraform als einzige Quelle der Wahrheit für alles – einschließlich der Bereitstellung – haben möchten, kann die Ressource cloudflare_worker_version Bindungen direkt verwalten. In diesem Modell bündelt Wrangler nur den Code, und Terraform übernimmt den Upload und die Verdrahtung der Bindungen:

resource "cloudflare_worker_version" "my_worker" {
  account_id         = var.cloudflare_account_id
  worker_id          = cloudflare_worker.my_worker.id
  main_module        = "index.js"
  compatibility_date = "2025-01-01"

  bindings = [
    {
      type         = "kv_namespace"
      name         = "MY_KV"
      namespace_id = cloudflare_workers_kv_namespace.my_kv.id
    },
    {
      type = "d1"
      name = "DB"
      id   = cloudflare_d1_database.my_db.id
    },
    {
      type        = "r2_bucket"
      name        = "MY_BUCKET"
      bucket_name = cloudflare_r2_bucket.my_bucket.name
    }
  ]
}

Mit diesem Ansatz:

  • wrangler deploy --dry-run --outdir dist bündelt den Code
  • Terraform lädt das Bundle hoch und verdrahtet alle Bindungen über Ressourcenreferenzen
  • Kein Brücken-Skript, keine Vorlage – Terraform löst IDs nativ auf

Kompromiss: Sie verlieren die lokalen Entwicklungsergonomien von Wrangler für die Bindungskonfiguration. Sie benötigen weiterhin ein wrangler.toml für wrangler dev, was eine gewisse Duplizierung bedeutet. Das Brückenmuster (oben) ist im Allgemeinen besser, wenn Entwickler regelmäßig wrangler dev verwenden, während der reine Terraform-Ansatz für CI-zentrierte Workflows geeignet ist, bei denen die lokale Entwicklung Mocks oder eine minimale Konfiguration verwendet.

Wählen Sie das Brückenmuster, wenn das Team regelmäßig wrangler dev ausführt. Wählen Sie von Terraform verwaltete Bindungen, wenn Terraform bereits das Bereitstellungstool ist und die lokale Entwicklung keine echten Bindungen benötigt.

Typsicherheit: wrangler types --check

Nach dem Generieren oder Aktualisieren der Wrangler-Konfiguration führen Sie wrangler types aus, um die TypeScript-Bindungen neu zu generieren. Verwenden Sie in CI --check, um den Build fehlschlagen zu lassen, wenn die Typen veraltet sind:

{
  "scripts": {
    "typecheck": "wrangler types --check",
    "predeploy": "npm run sync && npm run typecheck",
    "deploy": "wrangler deploy"
  }
}

Dies erfasst Diskrepanzen zwischen von Terraform verwalteten Ressourcen und den TypeScript-Typen des Workers vor der Bereitstellung.

Anpassung an ein bestehendes Projekt

Wenn Sie dieses Muster auf ein bereits bestehendes Projekt anwenden:

  1. Prüfen Sie, was existiert. Sehen Sie sich die aktuelle wrangler.toml/wrangler.jsonc auf hartcodierte Ressourcen-IDs an. Prüfen Sie, ob Terraform bereits verwendet wird. Sehen Sie sich die CI/CD-Konfiguration an.

  2. Identifizieren Sie die Ressourcen. Listen Sie jede Ressourcen-ID in der Wrangler-Konfiguration auf. Bestimmen Sie, welche bereits in Terraform sind und welche importiert werden müssen.

  3. Importieren Sie bestehende Ressourcen in Terraform mit terraform import, anstatt sie neu zu erstellen. Dies vermeidet Ausfallzeiten und Datenverlust.

  4. Erstellen Sie die Vorlage. Kopieren Sie die bestehende Wrangler-Konfiguration in wrangler.template.jsonc und ersetzen Sie hartcodierte IDs durch __PLACEHOLDER__-Token.

  5. Schreiben Sie das Brücken-Skript. Beginnen Sie mit dem Referenzskript oben und passen Sie Pfade und Ausgabenamen an.

  6. Verdrahten Sie package.json. Fügen Sie die Skripte sync, predeploy und deploy hinzu.

  7. Aktualisieren Sie CI/CD. Ersetzen Sie alle manuellen ID-Übergabe- oder Konfigurationsgenerierungsschritte durch npm run deploy.

  8. Fügen Sie wrangler.jsonc zu .gitignore hinzu, sobald die generierte Datei als funktionierend bestätigt wurde.

Häufige Muster

Mehrere Worker in einem Monorepo

Wenn ein Repository mehrere Worker enthält, erhält jeder seine eigene Vorlage, und das Sync-Skript verarbeitet alle:

project/
├── terraform/
├── workers/
│   ├── api/
│   │   ├── src/
│   │   └── wrangler.template.jsonc
│   └── cron/
│       ├── src/
│       └── wrangler.template.jsonc
├── scripts/
│   └── sync-wrangler-config.mjs
└── package.json

Das Sync-Skript iteriert über die Worker-Verzeichnisse und wendet dieselbe Token-Ersetzungslogik auf jede Vorlage an.

Secrets

Secrets sollten nicht durch das Brücken-Skript oder die Vorlage gehen. Verwenden Sie wrangler secret put (interaktiv) oder wrangler secret bulk (aus einer JSON-Datei, nützlich in CI). Secrets werden pro Worker in der Cloudflare-API verwaltet, nicht in Konfigurationsdateien.

In CI:

echo '{"API_KEY":"...","DB_PASSWORD":"..."}' | wrangler secret bulk

Vorschau-Umgebungen

Für PR-basierte Vorschau-Bereitstellungen kann das Sync-Skript ein Umgebungsargument akzeptieren:

{
  "scripts": {
    "deploy:preview": "npm run sync -- --env preview && wrangler deploy --env preview"
  }
}

Fehlerbehebung

SymptomWahrscheinliche Ursache
sync schlägt fehl mit "Fehler beim Lesen der Terraform-Ausgaben"Terraform wurde nicht angewendet, oder Sie sind im falschen Verzeichnis. Führen Sie zuerst terraform apply aus.
Nicht ersetzte __TOKENS__ in generierter KonfigurationTerraform-Ausgabenname stimmt nicht mit dem Token überein. Überprüfen Sie, ob die terraform output-Namen dem Token-Muster in Großbuchstaben entsprechen.
Wrangler-Deploy schlägt fehl mit "resource not found"Die Ressourcen-ID von Terraform ist veraltet oder stammt aus der falschen Umgebung. Führen Sie erneut terraform apply und dann npm run sync aus.
Drift zwischen Terraform-Zustand und CloudflareEine Ressource wurde außerhalb von Terraform geändert (möglicherweise durch Wrangler-Auto-Provisioning). Führen Sie terraform plan aus, um Drift zu erkennen und zu beheben.

Referenz

  • scripts/sync-wrangler-config.mjs – Das Brücken-Skript (siehe Verzeichnis scripts/ für eine produktionsreife Version mit Argumentparsing und Multi-Worker-Unterstützung)
  • references/terraform-wrangler-mapping.md – Kartiert Terra...

[truncated for translation]

Verwandte Skills