Files
eqt-network-meraki/modules/meraki-site/main.tf
T
Jose MartinezandClaude Sonnet 4.6 29614e1dc4 feat(bcn01-lab): onboard BCN01-LAB site and extend module with WAN2 support
- Add sites/BCN01-LAB with full Meraki configuration: VLANs, SSIDs,
  switch ports, 802.1X policy, firewall rules, WAN uplinks and warm spare
- Extend modules/meraki-site to support wan2_* fields in mx_wan_uplinks

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-29 07:02:06 +02:00

324 lines
12 KiB
Terraform
Executable File

terraform {
required_version = ">= 1.5.0"
required_providers {
meraki = {
source = "CiscoDevNet/meraki"
version = "1.9.0"
}
}
}
# Búsqueda automática de IDs (Data Sources)
data "meraki_organization" "org" {
name = var.organization_name
}
data "meraki_networks" "net" {
organization_id = data.meraki_organization.org.id
}
# Stacks de switches en la red (para resolución dinámica de seriales por nombre)
data "meraki_switch_stacks" "stacks" {
network_id = local.network_id
}
# Dispositivos de la red (para resolución dinámica de serial por nombre de switch)
data "meraki_network_devices" "devices" {
network_id = local.network_id
}
# Local para extraer el network_id exacto de la lista de redes
locals {
network_id = [for n in data.meraki_networks.net.items : n.id if n.name == var.network_name][0]
# Helper: expande un port_range en lista de port_ids
# Soporta: "1" -> ["1"] | "1-4" -> ["1","2","3","4"] | "2,3" -> ["2","3"] | "1-3,5,47" -> ["1","2","3","5","47"]
_expand_range = {
for config in concat(var.switch_port_configs, var.switch_stack_port_configs, var.switch_named_port_configs) :
"${try(config.serial, try(config.stack_name, config.switch_name))}:${config.port_range}" => flatten([
for segment in split(",", config.port_range) :
length(split("-", trimspace(segment))) == 2
? [for i in range(
tonumber(split("-", trimspace(segment))[0]),
tonumber(split("-", trimspace(segment))[1]) + 1
) : tostring(i)]
: [trimspace(segment)]
])
}
# Mapa de nombre de stack -> lista de seriales de sus miembros
stack_serials = {
for stack in data.meraki_switch_stacks.stacks.items :
stack.name => stack.serials
}
# Mapa de nombre de stack -> ID del stack
stack_ids = {
for stack in data.meraki_switch_stacks.stacks.items :
stack.name => stack.id
}
# Mapa de nombre de dispositivo -> serial
device_serials = {
for d in data.meraki_network_devices.devices.items :
d.name => d.serial
if d.name != null && d.name != ""
}
# Expande switch_port_configs (serial explícito) en entradas individuales
switch_ports_expanded = flatten([
for config in var.switch_port_configs : [
for port_id in local._expand_range["${config.serial}:${config.port_range}"] : {
key = "${config.serial}:${port_id}"
serial = config.serial
port_id = port_id
name = config.name
type = config.type
vlan = config.vlan
allowed_vlans = config.allowed_vlans
access_policy_type = config.access_policy_type
access_policy_number = config.access_policy_number
}
]
])
# Expande switch_stack_port_configs (por nombre de stack) en entradas individuales
# Aplica el mismo port_range a TODOS los miembros del stack
stack_ports_expanded = flatten([
for config in var.switch_stack_port_configs : [
for serial in local.stack_serials[config.stack_name] : [
for port_id in local._expand_range["${config.stack_name}:${config.port_range}"] : {
key = "${serial}:${port_id}"
serial = serial
port_id = port_id
name = config.name
type = config.type
vlan = config.vlan
allowed_vlans = config.allowed_vlans
access_policy_type = config.access_policy_type
access_policy_number = config.access_policy_number
}
]
]
])
# Expande switch_named_port_configs (por nombre de switch, resolución dinámica de serial)
named_ports_expanded = flatten([
for config in var.switch_named_port_configs : [
for port_id in local._expand_range["${config.switch_name}:${config.port_range}"] : {
key = "${local.device_serials[config.switch_name]}:${port_id}"
serial = local.device_serials[config.switch_name]
port_id = port_id
name = config.name
type = config.type
vlan = config.vlan
allowed_vlans = config.allowed_vlans
access_policy_type = config.access_policy_type
access_policy_number = config.access_policy_number
}
]
])
# Unión de todas las listas para el resource meraki_switch_port
all_ports = merge(
{ for p in local.switch_ports_expanded : p.key => p },
{ for p in local.stack_ports_expanded : p.key => p },
{ for p in local.named_ports_expanded : p.key => p }
)
}
# --- CONFIGURACIÓN GATEWAY (MX) ---
# 1. Activar VLANs en el Appliance (MX)
resource "meraki_appliance_vlans_settings" "activate_vlans" {
network_id = local.network_id
vlans_enabled = true
}
# 2. Crear las interfaces L3 y VLANs en el MX
# Solo se crean VLANs con subnet definida (VLANs L2-only como WAN se excluyen
# porque la API de Meraki exige que subnet sea una dirección IPv4 en formato CIDR)
resource "meraki_appliance_vlan" "mx_gateways" {
for_each = { for k, v in var.switch_vlans : k => v if v.subnet != null }
depends_on = [meraki_appliance_vlans_settings.activate_vlans]
network_id = local.network_id
vlan_id = each.key
name = each.value.name
subnet = each.value.subnet
appliance_ip = each.value.appliance_ip
reserved_ip_ranges = each.value.reserved_ip_ranges
dhcp_handling = each.value.dhcp_handling
}
# 3. SSIDs wireless
# SSIDs con WPA (psk, open-enhanced): incluyen wpa_encryption_mode
resource "meraki_wireless_ssid" "ssids" {
for_each = { for s in var.wireless_ssids : tostring(s.number) => s if s.auth_mode != "open" }
depends_on = [meraki_appliance_vlans_settings.activate_vlans]
network_id = local.network_id
number = each.value.number
name = each.value.name
enabled = each.value.enabled
visible = each.value.visible
auth_mode = each.value.auth_mode
psk = each.value.auth_mode == "psk" ? (each.value.psk != null ? each.value.psk : var.wifi_password_psk) : null
wpa_encryption_mode = each.value.wpa_encryption_mode
encryption_mode = each.value.encryption_mode
splash_page = each.value.splash_page
ip_assignment_mode = each.value.ip_assignment_mode
use_vlan_tagging = each.value.use_vlan_tagging
default_vlan_id = each.value.default_vlan_id
radius_servers = length(each.value.radius_servers) > 0 ? [
for server in each.value.radius_servers : {
host = server.host
port = server.port
secret = var.radius_secret
}
] : []
}
# SSIDs open (sin WPA): wpa_encryption_mode se omite — la API Meraki lo rechaza para auth_mode=open
resource "meraki_wireless_ssid" "ssids_open" {
for_each = { for s in var.wireless_ssids : tostring(s.number) => s if s.auth_mode == "open" }
depends_on = [meraki_appliance_vlans_settings.activate_vlans]
network_id = local.network_id
number = each.value.number
name = each.value.name
enabled = each.value.enabled
visible = each.value.visible
auth_mode = each.value.auth_mode
splash_page = each.value.splash_page
ip_assignment_mode = each.value.ip_assignment_mode
use_vlan_tagging = each.value.use_vlan_tagging
default_vlan_id = each.value.default_vlan_id
radius_servers = length(each.value.radius_servers) > 0 ? [
for server in each.value.radius_servers : {
host = server.host
port = server.port
secret = var.radius_secret
}
] : []
}
# 4. Reglas de firewall L3
resource "meraki_appliance_l3_firewall_rules" "reglas_firewall" {
network_id = local.network_id
rules = var.firewall_rules
}
# 4b. Configuración WAN estática de los MX (primary y spare)
# El serial se resuelve dinámicamente a partir del nombre del dispositivo
# usando el mismo data source meraki_network_devices que ya se usa para los switches
resource "meraki_device_management_interface" "mx_wan" {
for_each = { for mx in var.mx_wan_uplinks : mx.name => mx }
serial = local.device_serials[each.key]
wan1_static_ip = each.value.wan1_static_ip
wan1_static_subnet_mask = each.value.wan1_static_subnet_mask
wan1_static_gateway_ip = each.value.wan1_static_gateway_ip
wan1_static_dns = each.value.wan1_static_dns
wan2_static_ip = each.value.wan2_static_ip
wan2_static_subnet_mask = each.value.wan2_static_subnet_mask
wan2_static_gateway_ip = each.value.wan2_static_gateway_ip
wan2_static_dns = each.value.wan2_static_dns
}
# 4c. Warm Spare (HA) — VIP flotante entre primary y spare
resource "meraki_appliance_warm_spare" "ha" {
count = var.mx_warm_spare != null ? 1 : 0
network_id = local.network_id
enabled = var.mx_warm_spare.enabled
spare_serial = local.device_serials[var.mx_warm_spare.spare_name]
uplink_mode = var.mx_warm_spare.uplink_mode
virtual_ip1 = var.mx_warm_spare.virtual_ip1
virtual_ip2 = var.mx_warm_spare.virtual_ip2
}
# --- CONFIGURACIÓN SWITCHES (MS) ---
# 5. Políticas de acceso 802.1X
resource "meraki_switch_access_policy" "dot1x" {
for_each = { for p in var.switch_access_policies : p.name => p }
network_id = local.network_id
name = each.value.name
access_policy_type = each.value.access_policy_type
host_mode = each.value.host_mode
radius_accounting_enabled = each.value.radius_accounting_enabled
radius_testing_enabled = each.value.radius_testing_enabled
radius_coa_support_enabled = each.value.radius_coa_support_enabled
radius_failed_auth_vlan_id = each.value.radius_failed_auth_vlan_id
radius_re_authentication_interval = each.value.radius_re_authentication_interval
url_redirect_walled_garden_enabled = each.value.url_redirect_walled_garden_enabled
radius_servers = [
for server in each.value.radius_servers : {
host = server.host
port = server.port
secret = var.radius_secret
}
]
}
# 6. Configuración de puertos de switch
# switch_port_configs: serial explícito | switch_stack_port_configs: por nombre de stack
resource "meraki_switch_port" "ports" {
for_each = local.all_ports
depends_on = [meraki_switch_access_policy.dot1x]
serial = each.value.serial
port_id = each.value.port_id
name = each.value.name
type = each.value.type
vlan = each.value.vlan
allowed_vlans = each.value.type == "trunk" ? each.value.allowed_vlans : null
access_policy_type = each.value.access_policy_type
access_policy_number = each.value.access_policy_number
}
# 7. VLAN de gestión de los switches del site
resource "meraki_switch_settings" "mgmt_vlan" {
count = var.switch_management_vlan != null ? 1 : 0
network_id = local.network_id
vlan = var.switch_management_vlan
}
# 8. Interfaces de enrutamiento L3 en stacks (SVIs para acceso de gestión al Dashboard)
resource "meraki_switch_stack_routing_interface" "stack_interfaces" {
for_each = {
for i in var.stack_routing_interfaces :
"${i.stack_name}:${i.vlan_id}" => i
}
network_id = local.network_id
switch_stack_id = local.stack_ids[each.value.stack_name]
name = each.value.name
vlan_id = each.value.vlan_id
interface_ip = each.value.ip_address
subnet = each.value.subnet
default_gateway = each.value.default_gateway
}
# 9. Puertos del firewall MX
resource "meraki_appliance_port" "ports" {
for_each = { for p in var.appliance_ports : p.port_id => p }
network_id = local.network_id
port_id = each.value.port_id
enabled = each.value.enabled
type = each.value.type
vlan = each.value.vlan
allowed_vlans = each.value.type == "trunk" ? each.value.allowed_vlans : null
drop_untagged_traffic = each.value.drop_untagged_traffic
}