unkin-agent efeb148be7
ci/woodpecker/pr/plan Pipeline was successful
ci/woodpecker/pr/pre-commit Pipeline was successful
Recut the module to the NetBox catalogue only
Device records, interfaces, MACs and cabling are written by the
autodiscovery agent, and host names, roles and IP allocation by the UI, so
terraform declaring them fights both owners.

- drop the device, interface, MAC, per-host IP and net:<name> tag resources
- declare sites, device types, roles and platforms in their own config files
- add interface templates per device type and model the Omada SG3429X
- add per-site VLAN groups and VLANs, seeding vid 101 and 201 in syd1
- publish network profiles as site-scoped config contexts
- rename subnets to net-<a>-<b>-<c>-<d>-<len> and restore dns/domain/next_server
2026-10-03 17:26:43 +10:00

terraform-netbox

YAML-driven Terraform/Terragrunt for the NetBox catalogue: the stable reference data a host is planned against. Device records are not here.

Ownership split

Owner Objects
this repo sites, prefixes, DHCP pool ranges, gateways, managed IPs, VLAN groups + VLANs, device types (incl. switch models and their interface templates), manufacturers, device roles, platforms, custom-field definitions, network profiles
autodiscovery agent device records, interfaces, MAC addresses, switch cabling
UI host names, role assignment, IP allocation (inventory -> planned)

Writing a device from here would fight both of them, so the module declares no netbox_device.

Supersedes terraform-infra, which also drove Kea DHCP. Kea is managed by the kea-operator CRs in argocd-apps (apps/base/dhcp-system/cr/).

Layout

config/<region>/<dc>/sites.yaml                          # site slug -> name/description
config/<region>/<dc>/subnets/net-<a>-<b>-<c>-<d>-<len>.yaml
config/<region>/<dc>/device-types/<slug>.yaml            # manufacturer, model, port templates
config/<region>/<dc>/device-roles.yaml
config/<region>/<dc>/platforms.yaml                      # slug -> display name
config/<region>/<dc>/vlans.yaml                          # keyed per site
config/<region>/<dc>/network-profiles/<name>.yaml
config/<region>/<dc>/custom-fields.yaml                  # custom-field DEFINITIONS
config/<region>/<dc>/managed-ips.yaml
environments/<region>/<dc>/terragrunt.hcl
modules/netbox/

make plan / make apply reconcile the YAML against NetBox. State lives in Consul under infra/terraform/netbox/<region>/<dc>/state. A site that already exists in NetBox must be imported, not re-created: site names are unique.

Subnets (subnets/net-<a>-<b>-<c>-<d>-<len>.yaml)

The filename carries the network and mask length; the human label lives in description.

prefix: 198.18.15.0/24
description: syd1 production
site: syd1            # NetBox site slug
router: 254           # host-octet int -> gateway IP
dhcp:                 # omit for a NetBox-only prefix
  enabled: true
  start: 200          # host-octet int
  stop: 220
dns: [198.18.200.7]
domain: main.unkin.net
next_server: 198.18.19.19

Creates netbox_prefix, plus a netbox_ip_range with role dhcp and the gateway netbox_ip_address. dns, domain and next_server are read by no resource today; they are retained so a later change can drive Kea from this repo without re-deriving them.

Device types (device-types/<slug>.yaml)

ports declares interface templates as ranges, not 28 hand-written entries. NetBox instantiates them on every device created from the type.

manufacturer: TP-Link
model: Omada SG3429X
u_height: 1
ports:
  - name_format: two-gigabitEthernet 1/0/%d   # single %d
    type: 2.5gbase-t
    first: 1
    count: 24
  - name_format: ten-gigabitEthernet 1/0/%d
    type: 10gbase-x-sfpp
    first: 25
    count: 4

VLANs (vlans.yaml)

Keyed per site. Each site gets its own site-scoped netbox_vlan_group, so the same VID may exist in more than one site.

syd1:
  vid_ranges: [[1, 4094]]
  vlans:
    - vid: 101
      name: fabric_common_01

Network profiles (network-profiles/<name>.yaml)

A profile describes the interfaces a logical host should have. It is published as a netbox_config_context scoped to its sites (and roles, if given), so the UI reads profiles from the NetBox API rather than from git.

description: Dual-homed fabric host
sites: [syd1]
weight: 1000
interfaces:
  - name: fabric0
    vlan: 101                       # VID in the site's VLAN group
    subnet: net-198-18-13-0-24      # subnet key; the prefix is resolved into the context
    subinterfaces: []               # optional, same shape

Custom fields (custom-fields.yaml)

Field definitions for what bootapi reads: pxe_enabled, provision_template, domain, nameservers, gateway. content_types are NetBox app_label.model strings. pxe_enabled's value is deliberately not managed here — it is runtime state cleared by bootapi's /provisioned callback.

Providers + auth

Provider Source
netbox e-breuninger/netbox 4.3.0
vault hashicorp/vault 5.6.0

The NetBox token is minted per run from the Vault netbox engine (netbox/creds/terraform-netbox) and lease-revoked when the run ends; Consul state creds come from consul_root/au/syd1/creds/terraform-netbox. Both are granted to the terraform_netbox AppRole and the woodpecker_terraform_netbox k8s role in terraform-vault.

S
Description
YAML-driven Terraform for NetBox: prefixes, IPs, devices, platforms and custom fields
Readme 102 KiB