variable "keycloakoidc" { description = "Rancher Keycloak(OIDC) auth provider config, or null to leave unmanaged." type = object({ rancher_url = string # Rancher redirect URL, must end with /verify-auth client_id = string issuer = string # OIDC issuer (Authentik application URL) auth_endpoint = string # OIDC authorization endpoint scopes = optional(string, "openid profile email") # OIDC claim to read group names from (default Rancher uses "groups"). groups_field = optional(string, "groups") access_mode = optional(string, "unrestricted") enabled = optional(bool, true) # PEM CA cert Rancher trusts for the IdP's TLS on OIDC discovery. Leave null # to default to the internal PKI ca_chain read from Vault. certificate = optional(string, null) # 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) }) default = null } # Global role bindings mapping an Authentik group (permission group) to a Rancher # global role. The group name is the map key; the OIDC group principal id is # derived as keycloakoidc_group://. variable "global_role_bindings" { type = map(object({ global_role_id = string # e.g. "admin", "user" })) default = {} } # Rancher-local users (config/users/.yaml). Map key is the username. # rancher2_user requires a password, so every entry must point at a Vault kv-v2 # secret holding one; the value is read at plan time, so the secret must exist # before a user yaml is added. variable "users" { type = map(object({ name = optional(string, null) # display name, defaults to the username enabled = optional(bool, true) must_change_password = optional(bool, false) password_vault = object({ mount = string path = string key = optional(string, "password") }) # Global roles granted to this user, e.g. ["user", "admin"]. global_role_bindings = optional(list(string), []) })) default = {} # Rancher names each global role binding as an RFC 1123 label and rejects # anything else at apply time. The binding name is built from the username and # the role name verbatim, so both must already be compliant — fail the plan # with a clear message instead of munging the input and hoping. validation { condition = alltrue([ for username in keys(var.users) : can(regex("^[a-z0-9]([a-z0-9-]*[a-z0-9])?$", username)) ]) error_message = "Usernames must be RFC 1123 labels: lowercase alphanumerics and '-', starting and ending alphanumeric (no '.', '_', '@' or uppercase). Rename config/users/.yaml to a compliant username." } validation { condition = alltrue(flatten([ for user in values(var.users) : [ for role in user.global_role_bindings : can(regex("^[a-z0-9]([a-z0-9-]*[a-z0-9])?$", role)) ] ])) error_message = "Values in global_role_bindings must be RFC 1123 labels: lowercase alphanumerics and '-', starting and ending alphanumeric." } } # Custom Rancher global roles (config/roles/.yaml). Map key is the role # name, which is also the id other configs bind to via global_role_id. variable "global_roles" { type = map(object({ description = optional(string, null) new_user_default = optional(bool, false) inherited_cluster_roles = optional(list(string), []) rules = optional(list(object({ api_groups = optional(list(string), []) resources = optional(list(string), []) verbs = optional(list(string), []) non_resource_urls = optional(list(string), []) resource_names = optional(list(string), []) })), []) })) default = {} } # API tokens (config/tokens/.yaml). Map key names the Terraform resource # only. rancher2_token has no user selector (user_id is computed at 14.1.1), so # every token here is minted for the identity the provider authenticates as. variable "tokens" { type = map(object({ description = optional(string, null) # defaults to the map key ttl = optional(number, null) # seconds; 0/null = provider default renew = optional(bool, null) cluster_id = optional(string, null) # scope to one cluster, null = global })) default = {} } # Rancher settings (config/settings/.yaml). Map key is the setting name. variable "settings" { type = map(object({ value = string })) default = {} }