Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Manage core router tunnel interfaces #23

Merged
merged 1 commit into from
Aug 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 29 additions & 4 deletions main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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

Expand Down
86 changes: 86 additions & 0 deletions modules/available_interfaces/main.tf
Original file line number Diff line number Diff line change
@@ -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")}"
}
}
8 changes: 8 additions & 0 deletions modules/available_interfaces/providers.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
terraform {
required_providers {
netbox = {
source = "e-breuninger/netbox"
version = "~> 3.8.0"
}
}
}
52 changes: 42 additions & 10 deletions modules/device/tunnel.tf
Original file line number Diff line number Diff line change
@@ -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}"
Expand All @@ -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" {
Expand All @@ -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" {
Expand All @@ -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"
Expand Down
11 changes: 8 additions & 3 deletions modules/device/variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -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" {
Expand Down