9a6f775300
Scope now spans devices and provisioning, not just IPAM. - rename module/consul-path/role ipam -> infra - networks config (subnet binding + gateway/dns/search constants); prefixes tagged net:<name> - intent-only devices module: netbox_device + device_type/role/manufacturer, static or next-available IPs (sticky via ignore_changes), transitional bootstrap_mac interface for bootapi PXE keying - seed 6 pending hosts prodnxsr0014-0019 (mgmt IPs .14-.19, optiplex-3070) - ci/puppetdb_backfill.py: emit NetBox reality (serial/model/uuid/interfaces) for existing hosts Claude-Session: https://claude.ai/code/session_01JUoARVdmhxKQHyyyp1pxeT
100 lines
3.2 KiB
Terraform
100 lines
3.2 KiB
Terraform
locals {
|
|
# network name -> backing prefix id (from the subnets above).
|
|
network_prefix_id = { for nname, n in var.networks : nname => netbox_prefix.this[n.subnet].id }
|
|
|
|
# Distinct device inventory objects to create in NetBox.
|
|
device_models = toset([for d in var.devices : d.model_hint if d.model_hint != null])
|
|
device_roles = toset([for d in var.devices : d.role])
|
|
|
|
# Devices that carry a transitional bootstrap MAC (placeholder PXE interface).
|
|
bootstrap_devices = { for k, d in var.devices : k => d if d.bootstrap_mac != null }
|
|
|
|
# Flatten device network memberships: "<device>:<network>" -> {device, network, ip}.
|
|
device_networks = merge([
|
|
for dname, d in var.devices : {
|
|
for nname, ip in d.networks : "${dname}:${nname}" => {
|
|
device = dname
|
|
network = nname
|
|
ip = ip
|
|
}
|
|
}
|
|
]...)
|
|
|
|
static_device_ips = { for k, dn in local.device_networks : k => dn if dn.ip != "" }
|
|
auto_device_ips = { for k, dn in local.device_networks : k => dn if dn.ip == "" }
|
|
}
|
|
|
|
resource "netbox_manufacturer" "dell" {
|
|
count = length(local.device_models) > 0 ? 1 : 0
|
|
name = "Dell"
|
|
slug = "dell"
|
|
}
|
|
|
|
resource "netbox_device_type" "this" {
|
|
for_each = local.device_models
|
|
|
|
manufacturer_id = netbox_manufacturer.dell[0].id
|
|
model = title(replace(each.value, "-", " "))
|
|
slug = each.value
|
|
}
|
|
|
|
resource "netbox_device_role" "this" {
|
|
for_each = local.device_roles
|
|
|
|
name = each.value
|
|
slug = lower(replace(each.value, "::", "-"))
|
|
color_hex = "9e9e9e"
|
|
}
|
|
|
|
resource "netbox_device" "this" {
|
|
for_each = var.devices
|
|
|
|
name = each.key
|
|
device_type_id = netbox_device_type.this[each.value.model_hint].id
|
|
role_id = netbox_device_role.this[each.value.role].id
|
|
site_id = tonumber(data.netbox_site.this[each.value.site].id)
|
|
status = each.value.pxe ? "staged" : "active"
|
|
}
|
|
|
|
# Transitional: bootapi keys PXE on MAC and no discovery image exists yet, so seed a
|
|
# single placeholder interface carrying bootstrap_mac. Discovery replaces it with the
|
|
# real (model-specific) NICs later; the interface name is a neutral label, not a
|
|
# hardware assumption.
|
|
resource "netbox_device_interface" "bootstrap" {
|
|
for_each = local.bootstrap_devices
|
|
|
|
device_id = netbox_device.this[each.key].id
|
|
name = "bootstrap"
|
|
type = "1000base-t"
|
|
}
|
|
|
|
resource "netbox_mac_address" "bootstrap" {
|
|
for_each = local.bootstrap_devices
|
|
|
|
mac_address = each.value.bootstrap_mac
|
|
device_interface_id = netbox_device_interface.bootstrap[each.key].id
|
|
}
|
|
|
|
resource "netbox_ip_address" "device" {
|
|
for_each = local.static_device_ips
|
|
|
|
ip_address = each.value.ip
|
|
status = "active"
|
|
description = each.value.device
|
|
device_interface_id = try(netbox_device_interface.bootstrap[each.value.device].id, null)
|
|
}
|
|
|
|
resource "netbox_available_ip_address" "device" {
|
|
for_each = local.auto_device_ips
|
|
|
|
prefix_id = local.network_prefix_id[each.value.network]
|
|
status = "active"
|
|
description = each.value.device
|
|
device_interface_id = try(netbox_device_interface.bootstrap[each.value.device].id, null)
|
|
|
|
# Machines are never re-IPed (a replacement is a new machine); keep the allocation sticky.
|
|
lifecycle {
|
|
ignore_changes = [prefix_id]
|
|
}
|
|
}
|