unkinben bfbe42f97e Initial terraform-ipam: NetBox IPAM + Kea DHCP from per-subnet YAML
Manage NetBox prefixes and Kea DHCP scopes together from a single subnet
definition under config/<region>/<dc>/subnets/<name>.yaml.

- modules/ipam: netbox_prefix + netbox_ip_range (dhcp role) + gateway/managed
  netbox_ip_address + kea_subnet; tokens read from Vault KV via the vault provider
- environments/au/syd1 terragrunt env, Consul state backend
- config seed: five DHCP subnets (198.18.13-17) + local netbox-only 198.18.25.0/24
- .woodpecker pre-commit+plan on PR, apply on main; Makefile Vault auth pattern

Claude-Session: https://claude.ai/code/session_01JUoARVdmhxKQHyyyp1pxeT
2026-08-05 19:41:41 +10:00

terraform-ipam

YAML-driven Terraform/Terragrunt for IP address management: it manages NetBox IPAM prefixes and Kea DHCP scopes together from a single subnet definition.

Defining a subnet creates the NetBox prefix, optionally a DHCP scope in Kea (via the kea provider against the KeaAPI), marks the DHCP range in NetBox, and records the gateway.

Layout

config/<region>/<dc>/subnets/<name>.yaml   # one file per subnet, filename = subnet name
config/<region>/<dc>/managed-ips.yaml       # extra manually-managed IPs
environments/<region>/<dc>/terragrunt.hcl   # one Terragrunt env per region/dc
modules/ipam/                               # the subnet module

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

Subnet schema (config/<region>/<dc>/subnets/<name>.yaml)

prefix: 198.18.15.0/24
description: syd1 production
site: syd1            # NetBox site slug (optional; resolved to site_id)
dhcp:                 # omit the whole block for a NetBox-only prefix
  enabled: true
  start: 200          # host-octet int; pool/range start
  stop: 220           # host-octet int; pool/range stop
router: 254           # host-octet int; recorded as the gateway IP
dns: [198.18.200.7]
next_server: 198.18.19.19
domain: main.unkin.net

Resource mapping per subnet:

YAML Resource
prefix netbox_prefix (status=active, description, site_id)
dhcp.start/stop netbox_ip_range (role dhcp) + kea_subnet pool
router netbox_ip_address <router>/mask description gateway
dns/next_server/domain kea_subnet dns_servers / next_server / domain_name

Host-octet ints are expanded against the prefix with cidrhost(). A subnet with no dhcp block is NetBox-only (e.g. local.yaml = 198.18.25.0/24 — excluded from Kea). The dhcp role is a single shared netbox_ipam_role. PXE boot files are handled by Kea cluster client-classes, not here.

Managed IPs (config/<region>/<dc>/managed-ips.yaml)

A list of {ip, description} (ip in full CIDR form) rendered as netbox_ip_address.

Providers

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

Endpoints (module variable defaults): NetBox https://netbox.k8s.syd1.au.unkin.net, KeaAPI http://kea-api.dhcp-system.svc:8080 (ClusterIP, in-cluster only).

Tokens are read at plan time from Vault KV v2 kv/service/terraform/ipam with fields netbox_token and kea_token (the vault provider authenticates with the CI VAULT_TOKEN).

Deployment status / blockers

  • NetBox is not deployed on the cluster yet (only on the unmerged benvin/netbox argocd-apps branch; host netbox.k8s.syd1.au.unkin.net). Until it is live and reachable, plan/apply against NetBox will fail.
  • KeaAPI is not deployed yet (only on the unmerged benvin/kea-deployment argocd-apps branch; KeaAPI CR kea-api, ns dhcp-system, ClusterIP :8080).
  • Seed kv/service/terraform/ipam in Vault with netbox_token + kea_token before the pipeline can authenticate to either API.
  • Confirm the kea provider 0.0.1 is published to the terraform-unkin ArtifactAPI registry (release the provider repo if not).
S
Description
YAML-driven Terraform for NetBox IPAM, networks, and devices + Kea DHCP
Readme 86 KiB