From 61bd8a00f213081f4dcedb1470b058278c50fe9f Mon Sep 17 00:00:00 2001 From: Kochetkov S Date: Tue, 4 Aug 2026 16:06:31 +0300 Subject: [PATCH] ++ sync rabbitmq module with strict variable typing and password rotation guard --- modules/rabbitmq/main.tf | 13 ++++- modules/rabbitmq/variables.tf | 92 +++++++++++++++++++++++++++-------- 2 files changed, 84 insertions(+), 21 deletions(-) diff --git a/modules/rabbitmq/main.tf b/modules/rabbitmq/main.tf index 076b010..ee8365b 100644 --- a/modules/rabbitmq/main.tf +++ b/modules/rabbitmq/main.tf @@ -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" { diff --git a/modules/rabbitmq/variables.tf b/modules/rabbitmq/variables.tf index 5a10b11..b97648c 100644 --- a/modules/rabbitmq/variables.tf +++ b/modules/rabbitmq/variables.tf @@ -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" {