Add meraki_appliance_firewall_one_to_one_nat_rules resource to the meraki-site module and codify the existing Synology NAT rule found in BCN01-LAB Dashboard (57.133.120.190 → 10.2.56.3 via internet2). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
346 lines
12 KiB
Terraform
Executable File
346 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. Reglas NAT 1:1 del MX
|
|
resource "meraki_appliance_firewall_one_to_one_nat_rules" "nat_1to1" {
|
|
count = length(var.one_to_one_nat_rules) > 0 ? 1 : 0
|
|
network_id = local.network_id
|
|
|
|
rules = [
|
|
for rule in var.one_to_one_nat_rules : {
|
|
name = rule.name
|
|
public_ip = rule.public_ip
|
|
lan_ip = rule.lan_ip
|
|
uplink = rule.uplink
|
|
allowed_inbound = [
|
|
for ib in rule.allowed_inbound : {
|
|
protocol = ib.protocol
|
|
destination_ports = ib.destination_ports
|
|
allowed_ips = ib.allowed_ips
|
|
}
|
|
]
|
|
}
|
|
]
|
|
}
|
|
|
|
# 10. 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
|
|
}
|