Adds terraform/zitadel: the provisioning half the ArgoCD wiring left open. It declares 1 project, 19 project roles, 12 OIDC applications, 5 machine users plus keys, one API application for the gateway, project grants, per-agent claim metadata, and a disabled groups-claim action. Facts established while writing it: - Client IDs cannot be preserved. ZITADEL v4.19.2 CreateOIDCApplicationRequest has no client_id field, and provider 3.8.7 rejects a settable application id (terraform validate reports "Invalid or unknown key"). Plan §3.2 assumed a client_id-preserving re-point; every consumer now also changes client ID. - Role keys equal the existing group names, so the groups claim can be byte-identical and no RBAC file changes. v4.19.2 allows role keys of 1-200 characters with no character-set restriction. - Machine users get JWT profile keys, not passwords. Per-agent claims move to user metadata and reach tokens only through the groups-claim action. - The action stays gated: enable_groups_claim_action defaults to false because the action runtime API is unverified. Two validations stop a null script from reaching file(). Checks: terraform fmt -check -recursive clean, terraform validate 0 errors, plan reaches only the expected missing-credentials error, structural gate 0 error / 0 warning, and one reviewer round: BLOCK on a plan-time fileexists(null) and on the missing api-gw machine path, both fixed, re-reviewed to SIGN-OFF. State is local and git-ignored; it carries generated client secrets and machine keys. Move it to MinIO before a second operator applies. Not applied: no Zitadel instance is running yet.
54 lines
2.2 KiB
Terraform
54 lines
2.2 KiB
Terraform
# terraform/zitadel — identity configuration for the ZITADEL instance
|
|
#
|
|
# Scope: this root module declares the objects INSIDE ZITADEL (org, project,
|
|
# roles, OIDC applications, machine users, grants). It does NOT deploy ZITADEL
|
|
# itself — that is ArgoCD's job (`k8s/argocd/apps/30-security.yaml`, values in
|
|
# `k8s/infra/iam/zitadel-values.yaml`).
|
|
#
|
|
# Migration plan: ~/workplace/homelab-doc/ZITADEL_MIGRATION.html (outside git).
|
|
# This file implements §1.3 (roles), §1.4 (machine users), §2.4 (grants) and
|
|
# §6.2 (Terraform provider route). The claim-injection action of §1.3 lives in
|
|
# `actions.tf` and stays disabled until the live go/no-go gate proves it.
|
|
#
|
|
# Authentication to ZITADEL uses the `iam-admin` machine key that the chart's
|
|
# setup job writes to the `iam-admin` Secret in namespace iam:
|
|
#
|
|
# kubectl -n iam get secret iam-admin -o jsonpath='{.data.iam-admin\.json}' \
|
|
# | base64 -d > ~/.config/zitadel/iam-admin.json
|
|
# chmod 600 ~/.config/zitadel/iam-admin.json
|
|
#
|
|
# export ZITADEL_JWT_PROFILE_FILE=$HOME/.config/zitadel/iam-admin.json
|
|
# terraform -chdir=terraform/zitadel init
|
|
# terraform -chdir=terraform/zitadel plan
|
|
#
|
|
# State is LOCAL and git-ignored (`terraform/*.tfstate` in .gitignore). That is
|
|
# a deliberate first step, not the final answer: §8 risk 8 of the plan names
|
|
# state drift as a blast-radius item. Move the state to MinIO before a second
|
|
# operator ever runs `apply`. No secret is written to state except the generated
|
|
# client secrets and machine keys, which are marked sensitive — they still land
|
|
# in the state file, so treat that file as a secret.
|
|
terraform {
|
|
required_version = ">= 1.6.0"
|
|
|
|
required_providers {
|
|
zitadel = {
|
|
source = "zitadel/zitadel"
|
|
version = "3.8.7"
|
|
}
|
|
}
|
|
}
|
|
|
|
provider "zitadel" {
|
|
domain = var.zitadel_domain
|
|
port = var.zitadel_port
|
|
|
|
# JWT profile of the iam-admin machine user. Preferred over a PAT: it is
|
|
# short-lived per call and never sits in the environment as a bearer token.
|
|
# Set the path through ZITADEL_JWT_PROFILE_FILE.
|
|
jwt_profile_file = var.jwt_profile_file
|
|
|
|
# The instance serves plain HTTP behind ingress-nginx, which terminates TLS.
|
|
# Turn this on only if the provider must call the pod directly.
|
|
insecure = var.zitadel_insecure
|
|
}
|