Merge pull request #22 from its-corp/bcn01-lab

feat(bcn01-lab): onboard BCN01-LAB site
This commit is contained in:
Jose Martinez
2026-04-29 07:13:35 +02:00
committed by GitHub Enterprise
12 changed files with 457 additions and 0 deletions
+4
View File
@@ -220,6 +220,10 @@ resource "meraki_device_management_interface" "mx_wan" {
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
+4
View File
@@ -191,6 +191,10 @@ variable "mx_wan_uplinks" {
wan1_static_subnet_mask = optional(string, null)
wan1_static_gateway_ip = optional(string, null)
wan1_static_dns = optional(list(string), null)
wan2_static_ip = optional(string, null)
wan2_static_subnet_mask = optional(string, null)
wan2_static_gateway_ip = optional(string, null)
wan2_static_dns = optional(list(string), null)
}))
default = []
description = "Configuración de interfaz WAN1 estática para cada MX. El serial se resuelve automáticamente por nombre de dispositivo."
+25
View File
@@ -0,0 +1,25 @@
# This file is maintained automatically by "terraform init".
# Manual edits may be lost in future updates.
provider "registry.terraform.io/ciscodevnet/meraki" {
version = "1.9.0"
constraints = "1.9.0"
hashes = [
"h1:KmWz0JvCHdDd3AtuawxUwmW0VN3fooGw4CRaxiKHT5Y=",
"zh:0b9a7d32f331998a2a1531811667be44f799dfc03f6929f1414d2cab69f659f2",
"zh:179f791e2aa0ca6353541d90956548033b9ee0c880a096e48ce3ae3fe8a1862e",
"zh:2a1a32c6a8068c194e19859a7d88e0b95d0d9cbcf31444454b055ed62ace715a",
"zh:491812b74919d131f4ef3ba968d10b678275ed201428e2af7f53df40fd7e8cee",
"zh:4f5043f5165ee5199a61e4c15230d9f973ed0211a06600d75638f8369bac73fa",
"zh:5679d5a0d5dd370ff5d9321913f293f76be8f7ebc25e5cf1b45ceed9de803348",
"zh:58e1faba3d322bca68efb5cdac1ebe4e6d6f92834ebe5ccff8e491685620185c",
"zh:861b04ee4a498070cfb581488ddc3e90b25be895d35861c2a03a3b224d28e9b5",
"zh:890df766e9b839623b1f0437355032a3c006226a6c200cd911e15ee1a9014e9f",
"zh:92ee52e0dd3372e6dc2ed21bf5b21124b646c4c1037477e66eae87614d814036",
"zh:a3a851a3ce0c32b17506da0b9370f9d31df8e9d5ed4b422dc09ff5337d4b4192",
"zh:cdac168b00fa658ec68862677cb0b00f356095654e6b1d0df823c330492fa753",
"zh:dc454d6b1051891c99051b92e61015d244eb320a5a491cbffb770a005e448898",
"zh:f14317688e068e40dc11f609c4dc4f81cfa50fbaa43dcbe0117725f1149e9d89",
"zh:fe0544ac117d0c643559f042996fa32f243988bc48d4d49875abad6b326f0e2e",
]
}
+32
View File
@@ -0,0 +1,32 @@
# Pasos manuales — BCN01-LAB
Configuraciones que no pueden gestionarse via Terraform (limitaciones del provider v1.9.0)
y deben aplicarse directamente en el Meraki Dashboard.
---
## Client VPN (L2TP/IPSec)
**Dashboard:** Security & SD-WAN → Client VPN
| Parámetro | Valor |
|-----------|-------|
| Estado | Enabled |
| Subnet VPN | `10.2.58.0/23` |
| Authentication | RADIUS |
| RADIUS server | IP del Okta RADIUS Agent, puerto `1812` |
| RADIUS secret | Ver secret de Okta RADIUS Agent |
> **Nota:** El provider `CiscoDevNet/meraki` v1.9.0 no incluye el resource
> `meraki_appliance_vpn_client_vpn`. Cuando el provider lo soporte, esta
> configuración deberá migrarse a `sites/BCN01-LAB/vpn.auto.tfvars`.
---
## OWE (Opportunistic Wireless Encryption) — SSID EQT-CORPO
> **Note:** This is not a provider limitation. The provider manages `auth_mode = "open-enhanced"` correctly.
**Dashboard:** Wireless → SSIDs → EQT-CORPO → Edit settings → Security
After the first `terraform apply`, verify that **"Opportunistic Wireless Encryption"** is active in the Dashboard. In some cases Meraki requires a one-time manual confirmation click to enable OWE on a new SSID, even though Terraform has already pushed the correct configuration.
+13
View File
@@ -0,0 +1,13 @@
locals {
appliance_ports = [
{
# Port 7: trunk toward the switch stack
# Native VLAN 109 (MANAGEMENT), allows all VLANs
port_id = "7"
enabled = true
type = "trunk"
vlan = 109 # MANAGEMENT — native (untagged) VLAN
allowed_vlans = "all"
},
]
}
+67
View File
@@ -0,0 +1,67 @@
locals {
firewall_rules = [
{
comment = "Allow ACCESS to internet"
policy = "allow"
protocol = "any"
src_cidr = "10.2.32.0/21" # VLAN 100 - ACCESS
dest_cidr = "any"
},
{
comment = "Allow MANAGEMENT to internet (Meraki Dashboard access)"
policy = "allow"
protocol = "any"
src_cidr = "10.2.55.0/24" # VLAN 109 - MANAGEMENT
dest_cidr = "any"
},
{
comment = "Allow APs to internet (Meraki Dashboard access)"
policy = "allow"
protocol = "any"
src_cidr = "10.2.54.0/24" # VLAN 108 - APs
dest_cidr = "any"
},
{
comment = "Allow GUEST to internet"
policy = "allow"
protocol = "any"
src_cidr = "10.2.40.0/21" # VLAN 101 - GUEST
dest_cidr = "any"
},
{
comment = "Allow SERVERS to internet"
policy = "allow"
protocol = "any"
src_cidr = "10.2.56.0/24" # VLAN 110 - SERVERS
dest_cidr = "any"
},
{
comment = "Allow GUEST to SERVERS"
policy = "allow"
protocol = "any"
src_cidr = "10.2.40.0/21" # VLAN 101 - GUEST
dest_cidr = "10.2.56.0/24" # VLAN 110 - SERVERS
},
{
comment = "Test"
policy = "allow"
protocol = "any"
src_cidr = "10.212.0.0/16"
dest_cidr = "10.212.225.51/32,10.2.56.5/32"
},
{
comment = "Allow VPN outbound traffic"
policy = "allow"
protocol = "any"
src_cidr = "10.2.58.0/23" # Client VPN subnet
dest_cidr = "any"
},
{
comment = "Deny all other outbound traffic"
policy = "deny"
protocol = "any"
src_cidr = "any"
dest_cidr = "any"
},
]
}
+38
View File
@@ -0,0 +1,38 @@
terraform {
backend "s3" {} # Config injected via -backend-config in CI (see backend.hcl at repo root)
required_version = ">= 1.5.0"
required_providers {
meraki = {
source = "CiscoDevNet/meraki"
version = "1.9.0"
}
}
}
provider "meraki" {}
locals {
organization_name = "Adevinta Information Services SLU"
network_name = "BCN01-LAB"
}
module "bcn01_lab" {
source = "../../modules/meraki-site"
organization_name = local.organization_name
network_name = local.network_name
switch_vlans = local.switch_vlans
firewall_rules = local.firewall_rules
wireless_ssids = local.wireless_ssids
radius_secret = var.radius_secret
wifi_password_psk = var.wifi_password_psk
switch_access_policies = local.switch_access_policies
switch_port_configs = local.switch_port_configs
switch_stack_port_configs = local.switch_stack_port_configs
switch_named_port_configs = local.switch_named_port_configs
switch_management_vlan = local.switch_management_vlan
stack_routing_interfaces = local.stack_routing_interfaces
appliance_ports = local.appliance_ports
mx_wan_uplinks = local.mx_wan_uplinks
mx_warm_spare = local.mx_warm_spare
}
+31
View File
@@ -0,0 +1,31 @@
locals {
wireless_ssids = [
{
number = 0
name = "EQT-CORPO"
enabled = true
auth_mode = "8021x-radius"
encryption_mode = "wpa"
wpa_encryption_mode = "WPA3 Transition Mode"
splash_page = "None"
ip_assignment_mode = "Bridge mode"
use_vlan_tagging = true
default_vlan_id = 100
radius_servers = [
{ host = "10.2.56.5", port = 1812 }
]
},
{
number = 1
name = "EQT-GUEST"
enabled = true
auth_mode = "psk"
encryption_mode = "wpa"
wpa_encryption_mode = "WPA3 Transition Mode"
# Password injected via TF_VAR_wifi_password_psk (GitHub Secret: WIFI_PASSWORD_PSK)
ip_assignment_mode = "Bridge mode"
use_vlan_tagging = true
default_vlan_id = 101
},
]
}
+106
View File
@@ -0,0 +1,106 @@
locals {
# 802.1X access policies
# The RADIUS shared secret is injected from var.radius_secret — never put it here.
switch_access_policies = [
{
name = "DOT1X-CORPO"
access_policy_type = "802.1x"
host_mode = "Multi-Host"
radius_accounting_enabled = false
radius_re_authentication_interval = 0
url_redirect_walled_garden_enabled = false
radius_failed_auth_vlan_id = 101 # Fallback to GUEST if RADIUS unreachable
radius_servers = [
{ host = "10.2.56.5", port = 1812 }
]
},
]
# Ports by explicit serial — use when targeting a switch directly by serial number
# Example:
# switch_port_configs = [
# {
# serial = "XXXX-XXXX-XXXX"
# port_range = "1-20"
# type = "access"
# vlan = 100 # ACCESS — fallback if RADIUS doesn't assign a VLAN
# access_policy_type = "Custom access policy"
# access_policy_number = 1 # references DOT1X-CORPO above
# },
# ]
switch_port_configs = []
# Ports by stack name — Terraform resolves serials for all stack members automatically.
# The same port_range is applied to EVERY switch in the stack.
switch_stack_port_configs = [
{
stack_name = "bcn01-lab-stack01"
port_range = "47"
name = "UPLINK LAN BCN01-F04-MX01"
type = "trunk"
vlan = 109 # MANAGEMENT — native (untagged) VLAN
allowed_vlans = "all"
access_policy_type = "Open"
},
{
stack_name = "bcn01-lab-stack01"
port_range = "48"
name = "UPLINK LAN BCN01-F04-MX02"
type = "trunk"
vlan = 109 # MANAGEMENT — native (untagged) VLAN
allowed_vlans = "all"
access_policy_type = "Open"
},
]
# Ports by switch display name — targets a specific stack member without knowing its serial
switch_named_port_configs = [
{
switch_name = "eqt-lab-st01-sw01"
port_range = "1"
name = "Servers"
type = "access"
vlan = 110 # SERVERS
access_policy_type = "Open"
},
{
switch_name = "eqt-lab-st01-sw01"
port_range = "2,3"
name = "WAN"
type = "access"
vlan = 111 # WAN
access_policy_type = "Open"
},
{
switch_name = "eqt-lab-st01-sw02"
port_range = "2,3"
name = "WAN"
type = "access"
vlan = 111 # WAN
access_policy_type = "Open"
},
{
switch_name = "eqt-lab-st01-sw01"
port_range = "37"
name = "AP"
type = "trunk"
vlan = 108 # APs — native (untagged) VLAN
allowed_vlans = "100,101,108" # ACCESS + GUEST + APs
access_policy_type = "Open"
},
]
switch_management_vlan = 109
# L3 routing interfaces on the stack (SVIs for management access to Meraki Dashboard)
stack_routing_interfaces = [
{
stack_name = "bcn01-lab-stack01"
name = "MANAGEMENT"
vlan_id = 109
ip_address = "10.2.55.2"
subnet = "10.2.55.0/24"
default_gateway = "10.2.55.1"
},
]
}
+15
View File
@@ -0,0 +1,15 @@
# Sensitive variables — injected via TF_VAR_* environment variables (GitHub Secrets).
# Never put values for these in any .tf file.
variable "radius_secret" {
type = string
sensitive = true
default = ""
description = "RADIUS shared secret. Pass via TF_VAR_radius_secret (GitHub Secret: RADIUS_SECRET)."
}
variable "wifi_password_psk" {
type = string
sensitive = true
description = "PSK Wi-Fi password. Pass via TF_VAR_wifi_password_psk (GitHub Secret: WIFI_PASSWORD_PSK)."
}
+93
View File
@@ -0,0 +1,93 @@
locals {
switch_vlans = {
"100" = {
name = "ACCESS"
subnet = "10.2.32.0/21"
appliance_ip = "10.2.32.1"
reserved_ip_ranges = [
{ comment = "Static reserved", id = "static", start = "10.2.32.1", end = "10.2.32.49" }
]
}
"101" = {
name = "GUEST"
subnet = "10.2.40.0/21"
appliance_ip = "10.2.40.1"
reserved_ip_ranges = [
{ comment = "Static reserved", id = "static", start = "10.2.40.1", end = "10.2.40.49" }
]
}
"102" = {
name = "VC"
subnet = "10.2.48.0/24"
appliance_ip = "10.2.48.1"
reserved_ip_ranges = [
{ comment = "Static reserved", id = "static", start = "10.2.48.1", end = "10.2.48.49" }
]
}
"103" = {
name = "PRINTERS"
subnet = "10.2.49.0/24"
appliance_ip = "10.2.49.1"
reserved_ip_ranges = [
{ comment = "Static reserved", id = "static", start = "10.2.49.1", end = "10.2.49.49" }
]
}
"104" = {
name = "DISPLAYS"
subnet = "10.2.50.0/24"
appliance_ip = "10.2.50.1"
reserved_ip_ranges = [
{ comment = "Static reserved", id = "static", start = "10.2.50.1", end = "10.2.50.49" }
]
}
"105" = {
name = "BOOKING"
subnet = "10.2.51.0/24"
appliance_ip = "10.2.51.1"
reserved_ip_ranges = [
{ comment = "Static reserved", id = "static", start = "10.2.51.1", end = "10.2.51.49" }
]
}
"106" = {
name = "BADGE_READERS"
subnet = "10.2.52.0/24"
appliance_ip = "10.2.52.1"
reserved_ip_ranges = [
{ comment = "Static reserved", id = "static", start = "10.2.52.1", end = "10.2.52.49" }
]
}
"107" = {
name = "CCTV"
subnet = "10.2.53.0/24"
appliance_ip = "10.2.53.1"
reserved_ip_ranges = [
{ comment = "Static reserved", id = "static", start = "10.2.53.1", end = "10.2.53.49" }
]
}
"108" = {
name = "APs"
subnet = "10.2.54.0/24"
appliance_ip = "10.2.54.1"
reserved_ip_ranges = [
{ comment = "Static reserved", id = "static", start = "10.2.54.1", end = "10.2.54.49" }
]
}
"109" = {
name = "MANAGEMENT"
subnet = "10.2.55.0/24"
appliance_ip = "10.2.55.1"
dhcp_handling = "Do not respond to DHCP requests"
}
"110" = {
name = "SERVERS"
subnet = "10.2.56.0/24"
appliance_ip = "10.2.56.1"
dhcp_handling = "Do not respond to DHCP requests"
}
"111" = {
name = "WAN"
dhcp_handling = "Do not respond to DHCP requests"
# No subnet or appliance_ip — L2-only switching VLAN toward the ISP
}
}
}
+29
View File
@@ -0,0 +1,29 @@
locals {
# Warm Spare (HA) — floating VIP between primary and spare MX
mx_warm_spare = {
enabled = true
spare_name = "BCN01-F04-MX02"
uplink_mode = "virtual"
virtual_ip1 = "57.133.120.183" # Floating VIP on WAN1
virtual_ip2 = "57.133.120.182" # Floating VIP on WAN2
}
# Static WAN2 configuration for each MX (WAN1 disabled)
# Device serials are resolved automatically from the display name
mx_wan_uplinks = [
{
name = "BCN01-F04-MX01"
wan2_static_ip = "57.133.120.181"
wan2_static_subnet_mask = "255.255.255.240" # /28
wan2_static_gateway_ip = "57.133.120.177"
wan2_static_dns = ["8.8.8.8", "1.1.1.1"]
},
{
name = "BCN01-F04-MX02"
wan2_static_ip = "57.133.120.180"
wan2_static_subnet_mask = "255.255.255.240" # /28
wan2_static_gateway_ip = "57.133.120.177"
wan2_static_dns = ["8.8.8.8", "1.1.1.1"]
},
]
}