# 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///subnets/.yaml # one file per subnet (prefix + DHCP scope) config///networks/.yaml # logical networks devices join config///devices/.yaml # one file per device (intent only) config///managed-ips.yaml # extra manually-managed IPs environments///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///state`. ## Subnet schema (`subnets/.yaml`) ```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:`. ## Network schema (`networks/.yaml`) Per-network provisioning constants — hoisted here, never repeated per device. ```yaml 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/.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. ```yaml 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.