## Context
Wave 6a of the state-drift consolidation plan. The Domain wide catch all
Proxy Provider (pk=5) + its wrapping Application (slug=domain-wide-catch-all)
+ the embedded outpost (uuid 0eecac07-97c7-443c-8925-05f2f4fe3e47) have
run for a year as pure UI-created state. When the 2026-04-18 outpost SEV2
hit, it was harder to reason about the config than it should have been —
the only source of truth was the Authentik admin UI. Bringing the provider
+ application under Terraform means future changes are reviewable in PRs
and recoverable from git if the admin UI misbehaves.
## This change
Adds the `goauthentik/authentik` provider to the repo's central
`terragrunt.hcl` `required_providers` (side-effect: every stack can now
declare authentik resources; this stack is the only current consumer).
Stack-local `stacks/authentik/authentik_provider.tf` holds the provider
instance configuration + API token wiring + two resources + their flow
data-source lookups.
### Auth
- API token stored in Vault at `secret/authentik/tf_api_token`, identifier
`terraform-infra-stack`, intent=API, user=akadmin, no expiry. Rotatable
by rewriting the Vault KV + any running TF apply picks it up on next
plan.
### Imports (both landed zero-diff)
- `authentik_application.catchall` ← id `domain-wide-catch-all`
- `authentik_provider_proxy.catchall` ← id `5`
### Flow references
Authorization + invalidation flows are looked up via `data
"authentik_flow"` by slug (`default-provider-authorization-implicit-consent`
+ `default-provider-invalidation-flow`). Keeping them as data sources
rather than hardcoded UUIDs means a flow recreation (slug unchanged)
doesn't require an HCL edit.
### `lifecycle { ignore_changes }` scope
On `authentik_provider_proxy.catchall`:
- `property_mappings` (5 UUIDs), `jwt_federation_sources` (1 UUID) — the
live state references complex many-to-many relations that are easier
to manage from the Authentik UI than to serialise in HCL. Drift
suppressed.
- `skip_path_regex`, `internal_host`, all `basic_auth_*`,
`intercept_header_auth`, `access_token_validity` — either defaults or
UI-only tuning knobs that aren't part of Terraform's concern for this
catch-all provider.
On `authentik_application.catchall`:
- `meta_description`, `meta_launch_url`, `meta_icon`, `group`,
`backchannel_providers`, `policy_engine_mode`, `open_in_new_tab` —
cosmetic/non-functional attributes; the Authentik UI is the right
place to edit these and drift on them isn't interesting.
## What is NOT in this change
- Outpost-binding resource — the embedded outpost's provider list is a
single-row many-to-many that the Authentik UI manages cleanly; adding
TF there would fight the UI without reducing drift.
- Property mappings and JWT federation source — managed via UI, drift
suppressed. A future wave can bring them in when someone actually
wants to edit them through code review.
- Other Authentik entities (Flows, Stages, Groups, RBAC policies) —
same rationale: UI is the natural editing surface. Adopt incrementally
as they become interesting to code-review.
## Verification
```
$ cd stacks/authentik && ../../scripts/tg plan | grep Plan:
Plan: 0 to add, 1 to change, 0 to destroy.
# module.authentik.kubernetes_deployment.pgbouncer — pre-existing drift,
# unrelated to this commit (image_pull_policy Always -> IfNotPresent)
$ ../../scripts/tg state list | grep authentik_
authentik_application.catchall
authentik_provider_proxy.catchall
data.authentik_flow.default_authorization_implicit_consent
data.authentik_flow.default_provider_invalidation
```
## Reproduce locally
1. `git pull && cd stacks/authentik && ../../scripts/tg init`
2. Terraform pulls goauthentik/authentik provider (first time).
3. `tg plan` — expect only pgbouncer drift; authentik resources read-only.
Refs: Wave 6a of the state-drift consolidation (code-hl1)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
129 lines
3.2 KiB
HCL
129 lines
3.2 KiB
HCL
# Root Terragrunt configuration
|
|
# Provides DRY provider, backend, and variable loading for all stacks.
|
|
|
|
# Two-tier state backend:
|
|
# Tier 0 (bootstrap): local state, SOPS-encrypted in git — must exist before PG is reachable.
|
|
# Tier 1 (everything else): PG backend on CNPG cluster, native pg_advisory_lock.
|
|
locals {
|
|
tier0_stacks = ["infra", "platform", "cnpg", "vault", "dbaas", "external-secrets"]
|
|
stack_name = replace(path_relative_to_include(), "stacks/", "")
|
|
is_tier0 = contains(local.tier0_stacks, local.stack_name)
|
|
}
|
|
|
|
remote_state {
|
|
backend = local.is_tier0 ? "local" : "pg"
|
|
generate = {
|
|
path = "backend.tf"
|
|
if_exists = "overwrite_terragrunt"
|
|
}
|
|
config = local.is_tier0 ? {
|
|
path = "${get_repo_root()}/state/${path_relative_to_include()}/terraform.tfstate"
|
|
} : {
|
|
conn_str = get_env("PG_CONN_STR", "")
|
|
schema_name = local.stack_name
|
|
}
|
|
}
|
|
|
|
# Load config.tfvars (plaintext). Secrets come from Vault KV — authenticate via `vault login -method=oidc`.
|
|
terraform {
|
|
extra_arguments "common_vars" {
|
|
commands = get_terraform_commands_that_need_vars()
|
|
required_var_files = [
|
|
"${get_repo_root()}/config.tfvars"
|
|
]
|
|
}
|
|
|
|
extra_arguments "no_backup" {
|
|
commands = ["apply", "plan", "destroy", "import"]
|
|
arguments = ["-backup=-"]
|
|
}
|
|
|
|
extra_arguments "kube_config" {
|
|
commands = get_terraform_commands_that_need_vars()
|
|
arguments = [
|
|
"-var", "kube_config_path=${get_repo_root()}/config"
|
|
]
|
|
}
|
|
}
|
|
|
|
# Generate kubernetes + helm + cloudflare providers for all stacks.
|
|
# The infra stack overrides this to add the proxmox provider.
|
|
generate "k8s_providers" {
|
|
path = "providers.tf"
|
|
if_exists = "overwrite_terragrunt"
|
|
contents = <<EOF
|
|
terraform {
|
|
required_providers {
|
|
vault = {
|
|
source = "hashicorp/vault"
|
|
version = "~> 4.0"
|
|
}
|
|
cloudflare = {
|
|
source = "cloudflare/cloudflare"
|
|
version = "~> 4"
|
|
}
|
|
authentik = {
|
|
source = "goauthentik/authentik"
|
|
version = "~> 2024.10"
|
|
}
|
|
}
|
|
}
|
|
|
|
variable "kube_config_path" {
|
|
type = string
|
|
default = "~/.kube/config"
|
|
}
|
|
|
|
provider "kubernetes" {
|
|
config_path = var.kube_config_path
|
|
}
|
|
|
|
provider "helm" {
|
|
kubernetes = {
|
|
config_path = var.kube_config_path
|
|
}
|
|
}
|
|
|
|
provider "vault" {
|
|
address = "https://vault.viktorbarzin.me"
|
|
skip_child_token = true
|
|
}
|
|
EOF
|
|
}
|
|
|
|
# Generate Cloudflare provider config (separate file to avoid conflicts
|
|
# with stacks that override providers.tf, e.g. infra stack).
|
|
# DNS records are created per-service via ingress_factory's dns_type param.
|
|
generate "cloudflare_provider" {
|
|
path = "cloudflare_provider.tf"
|
|
if_exists = "overwrite_terragrunt"
|
|
contents = <<EOF
|
|
data "vault_kv_secret_v2" "cf_platform" {
|
|
mount = "secret"
|
|
name = "platform"
|
|
}
|
|
|
|
provider "cloudflare" {
|
|
api_key = data.vault_kv_secret_v2.cf_platform.data["cloudflare_api_key"]
|
|
email = "vbarzin@gmail.com"
|
|
}
|
|
EOF
|
|
}
|
|
|
|
# Generate shared tiers locals for all stacks.
|
|
# Previously duplicated in 67+ stacks; now defined once here.
|
|
generate "tiers" {
|
|
path = "tiers.tf"
|
|
if_exists = "overwrite_terragrunt"
|
|
contents = <<EOF
|
|
locals {
|
|
tiers = {
|
|
core = "0-core"
|
|
cluster = "1-cluster"
|
|
gpu = "2-gpu"
|
|
edge = "3-edge"
|
|
aux = "4-aux"
|
|
}
|
|
}
|
|
EOF
|
|
}
|