++ sync rabbitmq module with strict variable typing and password rotation guard

This commit is contained in:
Kochetkov S 2026-08-04 16:06:31 +03:00
parent 758226e29f
commit 61bd8a00f2
2 changed files with 84 additions and 21 deletions

View File

@ -17,7 +17,10 @@ locals {
for user in var.users : user.name => {
name = user.name
tags = try(user.tags, [])
password_length = try(tonumber(user.password_length), var.default_user_password_length)
# Строгий optional(number) без схемного дефолта отдаёт null, а не "нет
# ключа" - try() ловит только ошибки вычисления, не null-значения.
# Нужен coalesce, а не try, чтобы null действительно падал на дефолт.
password_length = coalesce(try(tonumber(user.password_length), null), var.default_user_password_length)
password_special = try(user.password_special, false)
}
}
@ -62,7 +65,9 @@ locals {
}
bindings = {
for binding in var.bindings : "${binding.vhost}:${binding.source}:${binding.destination_type}:${binding.destination}:${try(binding.properties_key, format("%s:%s", try(binding.routing_key, ""), sha1(jsonencode(try(binding.arguments, {})))))}" => {
# properties_key - optional(string) без схемного дефолта: при отсутствии
# приходит null, а не "нет ключа", поэтому coalesce, не try.
for binding in var.bindings : "${binding.vhost}:${binding.source}:${binding.destination_type}:${binding.destination}:${coalesce(binding.properties_key, format("%s:%s", try(binding.routing_key, ""), sha1(jsonencode(try(binding.arguments, {})))))}" => {
vhost = binding.vhost
source = binding.source
destination = binding.destination
@ -119,6 +124,10 @@ resource "random_password" "user" {
upper = true
lower = true
numeric = true
lifecycle {
ignore_changes = all
}
}
resource "rabbitmq_user" "this" {

View File

@ -43,62 +43,116 @@ variable "rabbitmq_policy" {
variable "vhosts" {
description = "RabbitMQ vhosts declared in infrastructure.yaml."
type = any
default = []
type = list(object({
name = string
description = optional(string)
tracing = optional(bool, false)
}))
default = []
}
variable "users" {
description = "RabbitMQ users declared in infrastructure.yaml."
type = any
default = []
type = list(object({
name = string
tags = optional(list(string), [])
password_length = optional(number)
password_special = optional(bool, false)
}))
default = []
}
variable "permissions" {
description = "RabbitMQ permissions declared in infrastructure.yaml."
type = any
default = []
type = list(object({
vhost = string
user = string
configure = optional(string, "")
write = optional(string, "")
read = optional(string, "")
}))
default = []
}
variable "topic_permissions" {
description = "RabbitMQ topic permissions declared in infrastructure.yaml."
type = any
default = []
type = list(object({
vhost = string
user = string
permissions = optional(list(object({
exchange = string
write = optional(string, "")
read = optional(string, "")
})), [])
}))
default = []
}
variable "exchanges" {
description = "RabbitMQ exchanges declared in infrastructure.yaml."
type = any
default = []
type = list(object({
name = string
vhost = string
type = optional(string, "topic")
durable = optional(bool, true)
auto_delete = optional(bool, false)
arguments = optional(any, {})
}))
default = []
}
variable "queues" {
description = "RabbitMQ queues declared in infrastructure.yaml."
type = any
default = []
type = list(object({
name = string
vhost = string
durable = optional(bool, true)
auto_delete = optional(bool, false)
arguments = optional(any, {})
}))
default = []
}
variable "bindings" {
description = "RabbitMQ bindings declared in infrastructure.yaml."
type = any
default = []
type = list(object({
vhost = string
source = string
destination = string
destination_type = string
routing_key = optional(string, "")
arguments = optional(any, {})
properties_key = optional(string)
}))
default = []
}
variable "policies" {
description = "RabbitMQ policies declared in infrastructure.yaml."
type = any
default = []
type = list(object({
name = string
vhost = string
pattern = string
apply_to = optional(string, "all")
priority = optional(number, 0)
definition = any
}))
default = []
}
variable "unmanaged_output_users" {
description = "Existing RabbitMQ users intentionally not managed, kept only for output compatibility."
type = any
type = list(string)
default = []
}
variable "unmanaged_output_permissions" {
description = "Existing RabbitMQ user/vhost pairs intentionally not managed, kept only for output compatibility."
type = any
default = []
type = list(object({
vhost = string
user = string
}))
default = []
}
variable "default_user_password_length" {