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
104 lines
4.3 KiB
Markdown
104 lines
4.3 KiB
Markdown
# 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`)
|
|
|
|
```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.
|
|
|
|
```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/<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.
|
|
|
|
```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.
|