Files
terraform-authentik/modules/authentik/variables.tf
T
unkin-agent 8ec8bbda8e
ci/woodpecker/pr/pre-commit Pipeline was successful
ci/woodpecker/pr/plan Pipeline was successful
Manage the agent API service account and token in Terraform
The Authentik API token used by estate automation was created by hand in the
UI and pasted into Vault, so it was undocumented, unauditable and impossible
to rotate reproducibly. Model it as config instead.

Add a service_accounts config kind, discovered from config/service_accounts/
like the other kinds. Each entry creates a service_account user, an RBAC role
carrying its global permissions, its API tokens, and (optionally) a kv-v2
write publishing each token key.

Add sa-agent-api granting view_outpost, view_token and view_token_key, with a
non-expiring api token agent-api-token published to kv/service/authentik/agent-api-token.
2026-08-29 22:58:40 +10:00

155 lines
6.3 KiB
Terraform

variable "groups" {
type = map(object({
name = string
is_superuser = optional(bool, false)
# PKs of existing parent groups. These must be literal group PKs, not keys
# into this map: authentik_group cannot reference itself.
parents = optional(list(string), null)
attributes = optional(map(string), {})
}))
default = {}
}
# Two-tier RBAC. Permission groups (akP-*) are the atomic units mapped to app
# roles and bound to applications for access. Role groups (akR-*) are what users
# are assigned to; each nests permission groups via `parents`, so a member of a
# role is an effective member of every permission it grants (Authentik membership
# propagates child -> parent). Split into two variables/resources so roles can
# reference permission group ids without the authentik_group self-reference error.
# The group name is the map key (the config filename); no `name` field needed.
variable "permission_groups" {
type = map(object({
# slug of the oauth2 application this permission grants *access* to; when set,
# a policy binding is created gating that app to this group (and its children).
application = optional(string, null)
attributes = optional(map(string), {})
}))
default = {}
}
variable "role_groups" {
type = map(object({
# keys into var.permission_groups that this role nests (becomes its parents).
permissions = optional(list(string), [])
is_superuser = optional(bool, false)
attributes = optional(map(string), {})
}))
default = {}
}
variable "providers_saml" {
type = map(object({
name = string
authorization_flow = string
invalidation_flow = string
acs_url = string
sp_binding = optional(string, "redirect")
audience = optional(string, "")
name_id_mapping = optional(string, null)
signing_kp = optional(string, null)
}))
default = {}
}
variable "providers_oauth2" {
type = map(object({
name = string
authorization_flow = string # flow slug, resolved to id via data.authentik_flow
invalidation_flow = string # flow slug, resolved to id via data.authentik_flow
client_type = optional(string, "confidential")
client_id = string
# client_secret is never committed. Point at a Vault kv-v2 secret whose
# `client_secret` key holds the value (seeded out of band); TF reads it.
client_secret_vault = optional(object({
mount = string
path = string
}), null)
# Managed identifiers of scope property mappings (e.g.
# goauthentik.io/providers/oauth2/scope-openid). Resolved to ids.
scope_mappings = optional(list(string), [])
# OAuth2 grant types the provider permits. Authentik 2026.5 added this as an
# explicit allow-list on the provider (models default = empty); an empty list
# rejects every authorize request with "invalid_request / The request is
# otherwise malformed". Default to the standard confidential web-app set so
# authorization_code (login) and refresh_token (offline access) work.
grant_types = optional(list(string), ["authorization_code", "refresh_token"])
# allowed_redirect_uris is list(map(string)); the API always stores a
# redirect_uri_type key, so it must be set here or every plan drifts.
redirect_uris = optional(list(object({
matching_mode = optional(string, "strict")
url = string
redirect_uri_type = optional(string, "authorization")
})), [])
signing_key = optional(string, null)
access_token_validity = optional(string, "minutes=10")
# Explicit launch URL for the app tile on the user dashboard ("My
# applications"). Null lets Authentik derive it from the first redirect_uri;
# set it to the app's UI to make the tile deterministic and to force an
# application re-save (which invalidates Authentik's cached access policy).
launch_url = optional(string, null)
# Optional app-role claim computed from (hierarchical) group membership: emit
# `claim` = the first matching rule's role, else `default`. The app requests
# `claim` as a scope and reads it (e.g. LiteLLM GENERIC_USER_ROLE_ATTRIBUTE).
# rules are evaluated in order, so list highest privilege first.
role_mappings = optional(object({
claim = string
default = string
rules = list(object({
group = string # permission group name (akP-*)
role = string # app role value
}))
}), null)
}))
default = {}
}
# Name of the RSA certificate keypair used to sign OAuth2 ID tokens (RS256) for
# every provider that does not set its own signing_key. Defaults to Authentik's
# built-in self-signed RSA keypair.
variable "oauth2_signing_key_name" {
type = string
default = "authentik Self-signed Certificate"
}
variable "providers_ldap" {
type = map(object({
name = string
bind_flow = string
unbind_flow = string
base_dn = string
certificate = optional(string, null)
tls_server_name = optional(string, null)
uid_start_number = optional(number, 2000)
gid_start_number = optional(number, 4000)
search_mode = optional(string, "direct")
bind_mode = optional(string, "direct")
mfa_support = optional(bool, true)
}))
default = {}
}
# Machine identities for automation (agents, CI). Each entry creates a service
# account user, an RBAC role carrying its global permissions, and any API tokens
# it needs. The username is the map key (the config filename).
variable "service_accounts" {
type = map(object({
name = optional(string, null) # display name; defaults to the key
description = optional(string, "")
# Global RBAC permissions granted via a dedicated role, in
# `<app_label>.<codename>` form (e.g. authentik_outposts.view_outpost).
permissions = optional(list(string), [])
# API tokens keyed by identifier. Set `vault` to publish the generated key
# into kv-v2 so consumers read it from Vault instead of an operator pasting it.
tokens = optional(map(object({
description = optional(string, "")
expiring = optional(bool, false)
vault = optional(object({
mount = string
path = string
key = optional(string, "token")
}), null)
})), {})
}))
default = {}
}