zitadel-configurator
A minimal CDK for Terraform (CDKTF) TypeScript app that provisions resources in a ZITADEL instance. The included example stack configures the ZITADEL provider and creates a demo organization ("geoffs-makers-guild").
This directory is intended for local development and experimentation against a local ZITADEL instance (via Docker Compose or a local Kubernetes cluster using kind + Helm).
Prerequisites
- Node.js >= 20.9 and npm
- CDKTF CLI (either install globally or use npx)
- Install:
npm i -g cdktf-cli@latest
- Or run via npx:
npx cdktf --help
- Install:
- One of the following ZITADEL runtimes:
- Docker + Docker Compose (quickest)
- or Kubernetes (kind), Helm, kubectl
- Optional: OpenSSL (to generate a strong master key)
Getting a local ZITADEL
Option A — Docker Compose (recommended to start):
-
From the repository root, create
.zitadel.env
with a strong master key. Example:ZITADEL_MASTERKEY=$(openssl rand -base64 32)
Alternatively, run
openssl rand -base64 32
and paste the result as the value ofZITADEL_MASTERKEY
. -
Start ZITADEL and its login UI:
docker compose -f compose.zitadel.yml up -d
-
Wait until services are healthy. The compose file will write two files to the repository root:
admin.pat
— a Personal Access Token (PAT) with IAM_OWNER role (use this for Terraform/ZITADEL provider authentication)login-client.pat
— a PAT for the login service
-
Useful URLs:
- API: http://localhost:8080
- Login UI: http://localhost:3000/ui/v2/login
Option B — Kubernetes (kind + Helm):
-
Scripts are provided under
k8s/
to spin up a local cluster and install Traefik, PostgreSQL, and ZITADEL with example values. -
From this directory:
cd cluster ./install-dev-platform.sh
-
The Helm example uses the host
pg-insecure.127.0.0.1.sslip.io
routed through Traefik, mapped to your local ports 80/443. -
To uninstall and clean up:
./uninstall-dev-platform.sh
Configure and use CDKTF
-
Install dependencies (inside this
zitadel-configurator
directory):npm install
-
Generate provider bindings (creates/updates the
.gen
folder):npm run get
-
Synthesize the Terraform JSON (outputs to
cdktf.out/
):npm run synth
You can also run CDKTF via
npx cdktf synth
. -
Deploy the stack (interactive approval by default):
npx cdktf deployCdktf
-
Destroy the stack when done:
npx cdktf destroy
Authentication and configuration
- The example code in
main.ts
reads a dotenv file one level up:../.zitadel.env
.- It currently expects
ZITADEL_MASTERKEY
in that file because the Docker Compose setup uses it to initialize ZITADEL.
- It currently expects
- The ZITADEL Terraform provider requires a token for API access (PAT or service account), not the master key.
-
When you start ZITADEL via Docker Compose in this repo, an admin PAT is written to
../admin.pat
(repository root). Use that for provider authentication. -
If you want to use the PAT with the current example, update
main.ts
to read the PAT and pass it to the provider'stoken
field. For example:import { readFileSync } from "node:fs"; const adminPat = readFileSync("../admin.pat", "utf8").trim(); new ZitadelProvider(this, "zitadel", { domain: "http://localhost:8080", // for Docker Compose token: adminPat, });
-
For the Kubernetes example, the domain in
main.ts
is set tohttps://pg-insecure.127.0.0.1.sslip.io
. Keep that if you are using the kind + Traefik + Helm setup; otherwise change it to your environment.
-
Commands reference
npm run get
— generates provider bindings fromcdktf.json
(.gen/
folder).npm run build
— type-checks and compiles TypeScript to JavaScript.npm run synth
— synthesizes Terraform JSON tocdktf.out/
.npx cdktf deployCdktf
— deploys the synthesized stack.npx cdktf destroy
— destroys the deployed resources.npm test
— runs Jest (withcdktf.Testing.setupJest
), useful for unit testing constructs.
Directory layout
main.ts
— CDKTF app entrypoint. Defines aTerraformStack
that configures the ZITADEL provider and creates a demo Org..gen/
— auto-generated provider constructs (created bycdktf get
).cdktf.json
— CDKTF project configuration (app
isnpx ts-node main.ts
).k8s/
— helper scripts to run a local K8s cluster and install ZITADEL with Helm.
Troubleshooting
Common Issues and Solutions
TLS Certificate Errors with Kubernetes Setup
If you encounter "x509: certificate signed by unknown authority" or "issuer does not match" errors:
-
Install cert-manager (if not already installed):
kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.15.3/cert-manager.yaml kubectl wait --for=condition=ready pod -l app.kubernetes.io/name=cert-manager -n cert-manager --timeout=300s
-
Create a self-signed certificate for the local domain:
# Create the certificate manually openssl req -x509 -newkey rsa:4096 -keyout tls.key -out tls.crt -days 365 -nodes -subj "/CN=machine.127.0.0.1.sslip.io" # Create Kubernetes TLS secret kubectl create secret tls zitadel-tls --cert=tls.crt --key=tls.key
-
Update main.ts configuration - Remove
insecure: true
flag and use proper JWT authentication:const provider = new ZitadelProvider(this, "zitadel", { domain: "machine.127.0.0.1.sslip.io", jwtProfileJson: JSON.stringify(JSON.parse(readFileSync(path.resolve("zitadel-admin-sa.json").toString(), 'utf-8'))), // Remove: insecure: true, // This causes issuer mismatch errors });
Other Common Issues
- Provider bindings missing (
.gen/providers/zitadel/...
): runnpm run get
. - Auth errors on deployCdktf: ensure you are using a valid service account JSON or PAT, not the master key.
machine.127.0.0.1.sslip.io
does not resolve: use the Docker Compose setup and set provider domain tohttp://localhost:8080
, or ensure your kind+Traefik install is running and ports 80/443 are mapped.- Node version errors: ensure Node >= 20.9 as specified in
package.json
.
Security
Do not commit secrets. Keep .zitadel.env
, admin.pat
, and other credentials out of version control.
License
MPL-2.0