Skip to content

Config Automation — Terraform Source of Truth, NetBox Downstream

Version: 2.0 — 2026-07-06 Terraform/git is the source of truth for every router's config and addressing. Configs are applied to the MikroTiks by the routeros provider; the live devices are then scraped into NetBox (your existing tool) so NetBox is the always-current documentation, IPAM reporting, and drift cross-check — never in the config critical path. Builds on TERRAFORM-PLAN.md (module restructure).

v1 of this doc had NetBox driving config generation. Reversed in v2: NetBox is downstream, matching the existing scrape→NetBox flow. Nothing needs to come from NetBox to build a config.


1. Direction of truth

graph LR
    ENG[Network eng] -->|edit intent| GIT[(git: terraform-mikrotik<br/>network.yaml + role modules)]
    GIT -->|terragrunt plan| MR[Merge Request + plan]
    MR -->|approve| APPLY[terragrunt apply<br/>routeros provider]
    APPLY --> DEV[MikroTik routers]
    DEV -->|your scrape tool| NB[(NetBox<br/>documentation · IPAM view · as-deployed)]
    NB -.->|read-only: what's free / drift check| ENG
  • git/Terraform — the record of intent: what every device should be. Nothing about a router is authored anywhere else.
  • routeros provider — converges devices to that intent (state + drift).
  • NetBox — populated from the devices by your scrape tool. It is the human-facing record of reality: topology, IPAM, diagrams, and the thing you diff against intent. It feeds engineers (what's free, what drifted); it does not feed Terraform.

2. What lives where

Concern Home (source of truth) NetBox role
Device config (OSPF, BGP, firewall, VPLS, communities) git / Terraform modules reflects as-deployed (scraped)
Addressing intent (loopbacks, /30s, venue /16s, VLANs, ASNs) git registry network.yaml shows as-deployed + what ranges are free (IPAM view)
Global data (DNS, RADIUS, NTP, mgmt lists) git network.yaml / _common reflected as device settings
Topology (who links to whom) git registry (drives config) scraped diagram = live picture
"What IP/loopback is free to allocate next" decided by engineer, recorded in git NetBox is the lookup (read-only)
As-deployed reality / drift evidence the devices NetBox holds it, diff vs git = drift
DHCP reservations (static leases) the MikroTik, hand-managed — out of Terraform scope may be scraped for docs, but never intent

DHCP scope boundary (important): Terraform owns the DHCP infrastructure — dhcp-server, dhcp-server network, pools, options. It does not manage /ip dhcp-server lease static reservations: those are edited directly on the device. So the modules must not declare lease resources, and both drift checks (§6) must exclude /ip dhcp-server lease from comparison — otherwise a nightly plan flags every hand-added reservation and an apply would delete them. Reservations changing on-device is expected, not drift.

The network.yaml registry (TERRAFORM-PLAN §3) is the linchpin: it holds the topology + allocations, and the modules derive full configs from it. NetBox is the mirror, not the mould.

3. The registry is the IPAM record (not NetBox)

Everything that would tempt you to "allocate in NetBox" is instead a network.yaml entry, versioned in git:

loopbacks:            # the §3.2 registry — git-authoritative, NetBox mirrors it
  CR-COLO-01: 10.255.255.1
  CR-SEELST-01: 10.255.255.231
rr_loopbacks: [10.255.255.1, 10.255.255.2]

venues:
  dirty-o-sheas:
    venue_id: 23                      # => 10.23.0.0/16, AS 64535, VPLS site-id 23
    uplinks:
      - { hub: CR-SEELST-01, port: sfp-sfpplus8, p2p: 10.254.71.20/30, link_class: fibre10g, primary: true }
    guest_vpls: true
    starlink_eligible: true

To add a venue: glance at NetBox (or network.yaml) for a free venue_id / /30, add the block, commit. The two-ends-of-a-/30 bug is gone because one registry entry generates both ends. AS = 64512 + venue_id, community tags, VPLS RTs — all derived from this (§4).

Optional NetBox-assisted allocation: a pre-commit helper can query NetBox read-only for the next free /30 in 10.254.Y.0/24 and the next free venue_id, so humans don't hand-scan — but the chosen value is written to network.yaml, and NetBox is never written to by Terraform. This keeps NetBox's IPAM strength available without putting it in the apply path.

4. Deriving configs from the registry (the generator logic)

Whether implemented in HCL (Terragrunt read_terragrunt_config + module logic) or a thin pre-processor emitting *.auto.tfvars.json, the derivations are the same — this is where the design rules live as code, fed by git not NetBox:

  • iBGP RR sessions: the two rr_loopbacks → every transport device gets two sessions. Add a device → one registry line, no peer-list edits.
  • eBGP venue sessions (both ends): each venue's uplinks → CE side (announce 10.V/16, default in, BFD) + PE side (from-venue-<id> filter accepting only 10.V/16, tag 65500:100 + 65500:911 if starlink_eligible, local-pref 50 on backup uplinks).
  • VPLS: guest_vpls / pppoe_site flags → spoke on the metro PE + hub on the colo CCRs, RD/RT/site-id from venue_id.
  • OSPF cost from link_class; community tags from prefix role; address-lists from device roles.
  • Validation before any apply: every /30 two-ended, every loopback unique + in range, no ASN/venue_id collision, every venue ≥1 uplink, MTU consistent along VPLS paths. Fail the pipeline, not the router.

The three worked configs (colo RR, metro PE, venue CE snippet) are the acceptance tests: the modules are correct when they reproduce those from network.yaml.

5. Execution model (unchanged from TERRAFORM-PLAN)

Role modules (baseline / transport / rr / venue-handoff / venue / vpls / bng), composed per device by role. Hybrid apply: resource-granular for well-supported config (interfaces, OSPF, BGP, firewall, DHCP, VLANs); templated .rsc script-push for routeros-provider gaps (BGP-VPLS, some LDP/BFD), keyed on a content hash. See TERRAFORM-PLAN §2 & §5.

6. CI / automation flow

  • Edit: engineer changes network.yaml or a module → push.
  • Plan → MR: CI runs terragrunt plan for each affected device (all devices when a global in network.yaml changes — that fans out). Plan posted to the MR.
  • Human gate: review what changes on which routers → approve. The one deliberate manual step: a git typo must not silently reconfigure the fleet.
  • Apply: merge → terragrunt apply per device in dependency order (RRs → metro → venues).
  • Scrape → NetBox: your existing tool ingests the now-changed devices; NetBox reflects the new reality (topology, IPAM, diagrams update).
  • Drift, two independent checks:
  • terragrunt plan -detailed-exitcode nightly across the fleet — non-empty plan with no open MR ⇒ someone hand-edited a router ⇒ alert. Excludes /ip dhcp-server lease (hand-managed reservations, §2).
  • NetBox vs git — because NetBox holds as-deployed and git holds intent, a scheduled diff (scraped state vs registry) is a second, provider-independent drift signal and an audit trail. Same exclusion: reservations are operational, not intent.
  • Oxidized /export backups run independently as the raw config record.

7. Worked example — one venue, git → live → documented

  1. git: add the dirty-o-sheas-style block to network.yaml (venue_id, uplink /30, flags). Commit.
  2. CI: terragrunt plan shows two devices change — the new venue CE and its metro PE (hand-off + VPLS spoke). Engineer approves.
  3. Apply: both converge; venue live, guest VPLS up.
  4. Scrape → NetBox: the new device, its /16, /30, VLANs and the new link appear in NetBox automatically — topology diagram and IPAM now show it, with zero manual NetBox entry.

No config authored by hand; NetBox stays current without being touched directly.

8. Build order

  1. Registry first: flesh out network.yaml from DISCOVERY.md + the loopback registry (DESIGN §3.2). This is now your IPAM source; NetBox will mirror it via scrape.
  2. Modules derive from it: build role modules; diff their output against the worked example configs until they match.
  3. Wire CI: plan-on-MR, apply-on-merge, nightly drift; confirm your scrape tool re-ingests post-apply.
  4. Optional niceties later: NetBox next-free helper; NetBox-vs-git drift diff.

9. When NetBox-as-source would be worth revisiting

Only if non-engineers need to add venues via NetBox forms, or IPAM allocation in network.yaml becomes error-prone at much larger scale. At ~40 routers with a git registry and a scrape tool already feeding NetBox, git-as-source is simpler and keeps NetBox out of the blast radius. Revisit if that calculus changes.