diff --git a/main.tf b/main.tf index 1452194..3a4b771 100644 --- a/main.tf +++ b/main.tf @@ -2,12 +2,35 @@ locals { devices = { for dev in data.netbox_devices.devices.devices : dev.name => dev } + core_devices = concat( + [for dev in data.netbox_devices.core_routers.devices : { + id = dev.device_id + name = dev.name + device = dev + }], + [for vm in data.netbox_virtual_machines.core_routers.vms : { + id = vm.vm_id + name = vm.name + vm = vm + }], + ) } resource "netbox_vpn_tunnel_group" "sites" { name = "site-tunnels" } +module "tunnel_interfaces" { + for_each = { for dev in local.core_devices : dev.name => dev } + + source = "./modules/available_interfaces" + + prefix = "tun" + device_id = each.value.id + device_type = can(each.value.vm) ? "vm" : "device" + targets = [for name, dev in local.devices : name] +} + module "device" { for_each = local.devices @@ -24,10 +47,12 @@ module "device" { tunnel_prefix_v4_id = data.netbox_prefix.tunnels_prefix_v4.id tunnel_prefix_v6_id = data.netbox_prefix.tunnels_prefix_v6.id - tunnel_peer_names = concat( - [for dev in data.netbox_devices.core_routers.devices : dev.name], - [for vm in data.netbox_virtual_machines.core_routers.vms : vm.name], - ) + core_tunnels = [for dev in local.core_devices : { + name = dev.name + device_id = dev.id + device_type = can(dev.vm) ? "vm" : "device" + if_name = module.tunnel_interfaces[dev.name].interface_names[each.key] + }] tunnel_prefix_role_id = data.netbox_ipam_role.transfer.id diff --git a/modules/available_interfaces/main.tf b/modules/available_interfaces/main.tf new file mode 100644 index 0000000..731e7fd --- /dev/null +++ b/modules/available_interfaces/main.tf @@ -0,0 +1,86 @@ +variable "device_id" { + type = number + description = "ID of the device to find interfaces for" +} + +variable "device_type" { + type = string + description = "Whether to look for interfaces on a device or vm" + default = "device" + validation { + condition = contains(["device", "vm"], var.device_type) + error_message = "Must be one of 'device' or 'vm'" + } +} + +variable "prefix" { + type = string + description = "interface prefix to find the next descendant for" +} + +variable "targets" { + type = list(string) + description = "names of targets to create interfaces for" +} + +data "netbox_device_interfaces" "existing" { + count = var.device_type == "device" ? 1 : 0 + + name_regex = "^${var.prefix}\\d+$" + + filter { + name = "device_id" + value = var.device_id + } +} + +data "netbox_interfaces" "existing" { + count = var.device_type == "vm" ? 1 : 0 + + name_regex = "^${var.prefix}\\d+$" + + filter { + name = "vm_id" + value = var.device_id + } +} + +locals { + if_names = concat( + [for dev in flatten(data.netbox_device_interfaces.existing[*].interfaces) : dev.name], + [for dev in flatten(data.netbox_interfaces.existing[*].interfaces) : dev.name], + ) + if_nums = [for name in local.if_names : parseint(one(regex("^${var.prefix}(\\d+)$", name)), 10)] + max_if_num = max(local.if_nums...) + current_targets_hash = sha1(join("-", var.targets)) +} + +resource "terraform_data" "targets_hash" { + for_each = { for name in var.targets : name => {} } + + input = local.current_targets_hash + lifecycle { + ignore_changes = [input] + } +} + +locals { + # targets that have been added in the current apply + relevant_targets = [for name in var.targets : name if terraform_data.targets_hash[name].output == local.current_targets_hash] + target_offsets = { for i, name in local.relevant_targets : name => i } +} + +resource "terraform_data" "ifnum" { + for_each = { for name in var.targets : name => {} } + + input = local.max_if_num + 1 + try(local.target_offsets[each.key], 0) + lifecycle { + ignore_changes = [input] + } +} + +output "interface_names" { + value = { + for name, data in terraform_data.ifnum : name => "${var.prefix}${coalesce(data.output, "-unknown")}" + } +} diff --git a/modules/available_interfaces/providers.tf b/modules/available_interfaces/providers.tf new file mode 100644 index 0000000..e4ec175 --- /dev/null +++ b/modules/available_interfaces/providers.tf @@ -0,0 +1,8 @@ +terraform { + required_providers { + netbox = { + source = "e-breuninger/netbox" + version = "~> 3.8.0" + } + } +} diff --git a/modules/device/tunnel.tf b/modules/device/tunnel.tf index 0cb8352..14ae4a7 100644 --- a/modules/device/tunnel.tf +++ b/modules/device/tunnel.tf @@ -1,5 +1,5 @@ resource "netbox_device_interface" "tunnels" { - for_each = { for i, name in var.tunnel_peer_names : name => i } + for_each = { for i, peer in var.core_tunnels : peer.name => i } type = "virtual" name = "tun${each.value}" @@ -8,25 +8,54 @@ resource "netbox_device_interface" "tunnels" { device_id = var.device_id } +resource "netbox_device_interface" "core" { + for_each = { for peer in var.core_tunnels : peer.name => peer if peer.device_type == "device" } + + type = "virtual" + name = each.value.if_name + label = "Tunnel ${var.name}" + description = "Tunnel to ${var.name}" + device_id = each.value.device_id + + mtu = 1476 +} + +resource "netbox_interface" "core" { + for_each = { for peer in var.core_tunnels : peer.name => peer if peer.device_type == "vm" } + + name = each.value.if_name + description = "Tunnel to ${var.name}" + virtual_machine_id = each.value.device_id + + mtu = 1476 +} + +locals { + core_interfaces = merge(netbox_device_interface.core, netbox_interface.core) +} + resource "netbox_available_prefix" "tunnels_v4" { - for_each = toset(var.tunnel_peer_names) + for_each = { for peer in var.core_tunnels : peer.name => {} } parent_prefix_id = var.tunnel_prefix_v4_id prefix_length = 31 status = "active" - description = "Tunnel from ${each.value} to ${var.name}" + description = "Tunnel from ${each.key} to ${var.name}" role_id = var.tunnel_prefix_role_id tenant_id = var.tenant_id } resource "netbox_ip_address" "remote_tunnel_address_v4" { - for_each = netbox_available_prefix.tunnels_v4 + for_each = { for peer in var.core_tunnels : peer.name => peer } - ip_address = "${cidrhost(each.value.prefix, 0)}/${each.value.prefix_length}" + ip_address = "${cidrhost(netbox_available_prefix.tunnels_v4[each.key].prefix, 0)}/${netbox_available_prefix.tunnels_v4[each.key].prefix_length}" status = "active" description = "Peer address of ${each.key} for ${var.name}" tenant_id = var.tenant_id + + object_type = each.value.device_type == "vm" ? "virtualization.vminterface" : "dcim.interface" + interface_id = local.core_interfaces[each.key].id } resource "netbox_ip_address" "local_tunnel_address_v4" { @@ -40,24 +69,27 @@ resource "netbox_ip_address" "local_tunnel_address_v4" { } resource "netbox_available_prefix" "tunnels_v6" { - for_each = toset(var.tunnel_peer_names) + for_each = { for peer in var.core_tunnels : peer.name => {} } parent_prefix_id = var.tunnel_prefix_v6_id prefix_length = 64 status = "active" - description = "Tunnel from ${each.value} to ${var.name}" + description = "Tunnel from ${each.key} to ${var.name}" role_id = var.tunnel_prefix_role_id tenant_id = var.tenant_id } resource "netbox_ip_address" "remote_tunnel_address_v6" { - for_each = netbox_available_prefix.tunnels_v6 + for_each = { for peer in var.core_tunnels : peer.name => peer } - ip_address = "${cidrhost(each.value.prefix, 1)}/${each.value.prefix_length}" + ip_address = "${cidrhost(netbox_available_prefix.tunnels_v6[each.key].prefix, 1)}/${netbox_available_prefix.tunnels_v6[each.key].prefix_length}" status = "active" description = "Peer address of ${each.key} for ${var.name}" tenant_id = var.tenant_id + + object_type = each.value.device_type == "vm" ? "virtualization.vminterface" : "dcim.interface" + interface_id = local.core_interfaces[each.key].id } resource "netbox_ip_address" "local_tunnel_address_v6" { @@ -71,7 +103,7 @@ resource "netbox_ip_address" "local_tunnel_address_v6" { } resource "netbox_vpn_tunnel" "core" { - for_each = { for i, name in var.tunnel_peer_names : name => i } + for_each = { for peer in var.core_tunnels : peer.name => {} } name = "${each.key}-${var.name}" encapsulation = "gre" diff --git a/modules/device/variables.tf b/modules/device/variables.tf index c038ba4..9725db1 100644 --- a/modules/device/variables.tf +++ b/modules/device/variables.tf @@ -18,9 +18,14 @@ variable "sites_prefix_v6_id" { description = "Netbox ID of prefix (v6) to create site prefix in" } -variable "tunnel_peer_names" { - type = list(string) - description = "names of tunnel peers" +variable "core_tunnels" { + type = list(object({ + name = string + device_id = string + device_type = string + if_name = string + })) + description = "info about tunnel peers" } variable "tunnel_group_id" {