unkinben a025819fcd
ci/woodpecker/pr/pre-commit Pipeline was successful
ci/woodpecker/pr/plan Pipeline failed
Mint the NetBox token from the Vault netbox engine
Why:
- The netbox provider authenticated with a static netbox_token seeded by hand
  into KV; the vault-plugin-secrets-netbox engine mints a per-run ephemeral
  token that is lease-revoked when the run ends, removing the manual seed.

How:
- Read netbox/creds/terraform-infra via vault_generic_secret and pass the minted
  netbox_token to the netbox provider.
- Keep kea_token from KV for now (follow-up: its own ephemeral-token engine).

Depends on the netbox engine mount + netbox/creds/terraform-infra role/policy in
terraform-vault being applied first.
2026-08-09 00:18:47 +10:00

terraform-infra

YAML-driven Terraform/Terragrunt for infrastructure state that belongs in NetBox + Kea: subnets (NetBox prefixes + Kea DHCP scopes), logical networks, and physical devices. NetBox is authoritative for all IP/interface data; this repo declares intent and lets discovery/PuppetDB fill hardware reality.

Renamed from terraform-ipam — scope now spans devices and provisioning, not just IPAM. The old repo name is archived by the terraform-git rename.

Layout

config/<region>/<dc>/subnets/<name>.yaml    # one file per subnet (prefix + DHCP scope)
config/<region>/<dc>/networks/<name>.yaml   # logical networks devices join
config/<region>/<dc>/devices/<host>.yaml    # one file per device (intent only)
config/<region>/<dc>/managed-ips.yaml       # extra manually-managed IPs
environments/<region>/<dc>/terragrunt.hcl   # one Terragrunt env per region/dc
modules/infra/                              # the module
ci/puppetdb_backfill.py                     # reality backfill generator (local/in-cluster)

make plan / make apply reconcile the YAML against NetBox + Kea. State lives in Consul under infra/terraform/infra/<region>/<dc>/state.

Subnet schema (subnets/<name>.yaml)

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

Creates netbox_prefix (+ netbox_ip_range role dhcp + kea_subnet when dhcp set, + gateway netbox_ip_address). A prefix backing a network is tagged net:<name>.

Network schema (networks/<name>.yaml)

Per-network provisioning constants — hoisted here, never repeated per device.

subnet: net-198-18-15   # which subnet's prefix backs this network
gateway: 198.18.15.254
dns: [198.18.19.16]
search: main.unkin.net

Device schema (devices/<host>.yaml) — intent only

Declare only human intent. Serial, real interface names (which vary by model — OptiPlex 3060 = enp1s0, 3050/7080 = enp2s0), MACs and inventory are owned by the first-boot discovery image / PuppetDB, never hardcoded here.

site: syd1
role: roles::base                 # puppet mgmt_class / ENC role -> NetBox device role
model_hint: optiplex-3070         # NetBox device type; discovery confirms via dmidecode
provision:
  profile: almalinux9-dell_3070   # cobbler profile / kickstart template
  platform: almalinux9
networks:
  mgmt: 198.18.15.14/24           # requested IP; "" = next-available from the prefix
pxe: true
bootstrap_mac: a4:bb:6d:xx:xx:xx  # TRANSITIONAL — see below

Mapping: netbox_device (+ netbox_manufacturer/netbox_device_type/ netbox_device_role), netbox_ip_address (static) or netbox_available_ip_address (next-available, ignore_changes so a machine is never re-IPed). bootstrap_mac is transitional: it seeds one placeholder netbox_device_interface + netbox_mac_address so bootapi can key the PXE boot on MAC until the discovery image exists — remove it once discovery/backfill populates the real NICs.

PuppetDB reality backfill

ci/puppetdb_backfill.py sweeps already-provisioned hosts and emits their hardware reality (serial/model/UUID + every interface's MAC/IPs, incl. overlay/loopback addresses Cobbler lacks) as reviewable YAML — the NetBox reality side, not the intent above. PuppetDB needs no auth but is not reachable from CI; run it in-cluster/from puppet infra. Wiring its output into NetBox is a follow-up (issue).

Providers

Provider Source Notes
netbox e-breuninger/netbox 4.3.0 OpenTofu registry
kea artifactapi.k8s.syd1.au.unkin.net/terraform-unkin/kea 0.0.1 cluster_ref=kea
vault hashicorp/vault 5.6.0 reads NetBox + Kea tokens

Tokens: Vault KV v2 kv/service/terraform/infra fields netbox_token + kea_token.

Blockers

  • NetBox not deployed yet (unmerged benvin/netbox argocd branch); NetBox sites must pre-exist. KeaAPI not deployed yet (argocd-apps PR #333). Seed kv/service/terraform/infra before the pipeline can auth.
S
Description
YAML-driven Terraform for NetBox IPAM, networks, and devices + Kea DHCP
Readme 86 KiB