Manage Rancher users, global roles, tokens and settings
Extend the yaml-discovery config to four more rancher2 kinds so Rancher objects beyond the OIDC auth provider stop being unmanaged clickops. - Discover config/users, config/roles, config/tokens and config/settings in config/config.hcl and pass them through terragrunt.hcl - Add rancher2_user (password from Vault kv-v2) plus per-user rancher2_global_role_binding on "<username>/<role>" keys - Add rancher2_global_role with dynamic rules blocks - Add rancher2_token, documenting that the provider has no user selector - Add rancher2_setting and seed config/settings/server-url.yaml - Document every yaml schema in the README
This commit is contained in:
@@ -52,3 +52,93 @@ resource "rancher2_global_role_binding" "group" {
|
||||
|
||||
depends_on = [rancher2_auth_config_keycloak_oidc.this]
|
||||
}
|
||||
|
||||
# Rancher-local users. rancher2_user makes `password` required, so each user's
|
||||
# password comes from a Vault kv-v2 secret rather than the repo. Vault data
|
||||
# sources resolve at plan time: seed the secret before adding the yaml.
|
||||
data "vault_kv_secret_v2" "user_password" {
|
||||
for_each = var.users
|
||||
|
||||
mount = each.value.password_vault.mount
|
||||
name = each.value.password_vault.path
|
||||
}
|
||||
|
||||
resource "rancher2_user" "this" {
|
||||
for_each = var.users
|
||||
|
||||
username = each.key
|
||||
name = coalesce(each.value.name, each.key)
|
||||
enabled = each.value.enabled
|
||||
must_change_password = each.value.must_change_password
|
||||
password = data.vault_kv_secret_v2.user_password[each.key].data[each.value.password_vault.key]
|
||||
}
|
||||
|
||||
# Flatten users -> their global roles into "<username>/<role>" keys so adding or
|
||||
# removing one role never re-indexes the others.
|
||||
locals {
|
||||
user_global_role_bindings = merge([
|
||||
for username, user in var.users : {
|
||||
for role in user.global_role_bindings :
|
||||
"${username}/${role}" => {
|
||||
username = username
|
||||
global_role_id = role
|
||||
}
|
||||
}
|
||||
]...)
|
||||
}
|
||||
|
||||
resource "rancher2_global_role_binding" "user" {
|
||||
for_each = local.user_global_role_bindings
|
||||
|
||||
# Binding name must be an RFC 1123 label, so lowercase it and swap the key's
|
||||
# "/" separator for "-".
|
||||
name = "akuser-${lower(replace(each.key, "/", "-"))}"
|
||||
global_role_id = each.value.global_role_id
|
||||
user_id = rancher2_user.this[each.value.username].id
|
||||
}
|
||||
|
||||
# Custom global roles. Rules are optional: a role with none grants nothing on its
|
||||
# own and is useful purely as a container for inherited_cluster_roles.
|
||||
resource "rancher2_global_role" "this" {
|
||||
for_each = var.global_roles
|
||||
|
||||
name = each.key
|
||||
description = each.value.description
|
||||
new_user_default = each.value.new_user_default
|
||||
inherited_cluster_roles = each.value.inherited_cluster_roles
|
||||
|
||||
dynamic "rules" {
|
||||
for_each = each.value.rules
|
||||
content {
|
||||
api_groups = rules.value.api_groups
|
||||
resources = rules.value.resources
|
||||
verbs = rules.value.verbs
|
||||
non_resource_urls = rules.value.non_resource_urls
|
||||
resource_names = rules.value.resource_names
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# CAVEAT: rancher2_token has no user selector — `user_id` is computed by the
|
||||
# provider at 14.1.1, not settable. Every token declared here is minted for the
|
||||
# identity the rancher2 provider authenticates as (the CI admin service account),
|
||||
# NOT for any user in config/users/. There is no way to mint a token on another
|
||||
# user's behalf through this provider; that has to be done by that user.
|
||||
# The token/secret_key values land in Terraform state, so treat state as secret.
|
||||
resource "rancher2_token" "this" {
|
||||
for_each = var.tokens
|
||||
|
||||
description = coalesce(each.value.description, each.key)
|
||||
ttl = each.value.ttl
|
||||
renew = each.value.renew
|
||||
cluster_id = each.value.cluster_id
|
||||
}
|
||||
|
||||
# Rancher settings, e.g. server-url. Rancher ships defaults for these, so an
|
||||
# entry here takes over an existing setting rather than creating a new one.
|
||||
resource "rancher2_setting" "this" {
|
||||
for_each = var.settings
|
||||
|
||||
name = each.key
|
||||
value = each.value.value
|
||||
}
|
||||
|
||||
@@ -32,3 +32,62 @@ variable "global_role_bindings" {
|
||||
}))
|
||||
default = {}
|
||||
}
|
||||
|
||||
# Rancher-local users (config/users/<username>.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 = {}
|
||||
}
|
||||
|
||||
# Custom Rancher global roles (config/roles/<name>.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/<name>.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/<name>.yaml). Map key is the setting name.
|
||||
variable "settings" {
|
||||
type = map(object({
|
||||
value = string
|
||||
}))
|
||||
default = {}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user