Back to Learn

NetBox, Terraform & Batfish โ€” network IaC

The infrastructure-as-code stack for networks: NetBox as the authoritative source of truth, Terraform to declare and provision resources, and Batfish to analyse and validate configuration before it ever touches a device.

Source of truth
Terraform
NetBox
Batfish
IaC
Validation
NetBox is the source of truth for IPAM & DCIM
Terraform declares resources idempotently
Batfish analyses config offline before deploy

Setup, key commands & run examples

Step 0 โ€” run NetBox
Source of truth

Spin up NetBox locally with the community docker-compose to start modelling sites, devices, IPs and prefixes.

Step 0 โ€” run NetBox
bash
git clone https://github.com/netbox-community/netbox-docker
cd netbox-docker
docker compose pull
docker compose up -d
# UI at http://localhost:8000
netbox_query.py โ€” read intent
pynetbox

Use pynetbox to pull source-of-truth data (e.g. prefixes for a site) that downstream templates and Terraform consume.

netbox_query.py โ€” read intent
python
import pynetbox

nb = pynetbox.api("http://localhost:8000", token="YOUR_TOKEN")

for prefix in nb.ipam.prefixes.filter(site="dc1"):
    print(prefix.prefix, prefix.description)
main.tf โ€” declare resources
Terraform

Terraform providers (e.g. the NetBox provider) let you declare desired state. terraform plan diffs, apply reconciles.

main.tf โ€” declare resources
hcl
terraform {
  required_providers {
    netbox = {
      source = "e-breuninger/netbox"
    }
  }
}

resource "netbox_prefix" "dc1_mgmt" {
  prefix      = "10.0.0.0/24"
  status      = "active"
  description = "DC1 management"
}
terraform_apply.sh โ€” plan & apply
Workflow

Always plan before apply so the change set is reviewed. Store state remotely for team workflows.

terraform_apply.sh โ€” plan & apply
bash
terraform init
terraform plan -out=tfplan
terraform apply tfplan
batfish_check.py โ€” validate config
Batfish

Batfish builds a vendor-neutral model of your configs and answers questions (undefined refs, reachability) without touching live gear.

batfish_check.py โ€” validate config
python
from pybatfish.client.session import Session

bf = Session(host="localhost")
bf.set_network("prod")
bf.init_snapshot("./configs", name="snap1", overwrite=True)

issues = bf.q.undefinedReferences().answer().frame()
print(issues)

Best practices

  • One source of truth

    Model intent in NetBox and generate everything downstream from it โ€” never let device config drift become the truth.

  • Plan before apply

    Gate terraform apply behind a reviewed terraform plan in CI; never apply unseen changes to live infrastructure.

  • Store Terraform state remotely

    Use a remote backend with locking so concurrent runs don't corrupt state in team environments.

  • Validate with Batfish in CI

    Run Batfish questions (undefined refs, ACL reachability) on every PR to catch logic errors before deployment.

Hands-on exercises

Guided practice tasks โ€” copy the starter snippet, run it against a lab device, and extend it to meet the goal.

Exercise 1
Read prefixes from NetBox

Goal: Use pynetbox to list all active prefixes for a site and print prefix + description.

exercise_1
python
import pynetbox

nb = pynetbox.api("http://localhost:8000", token="YOUR_TOKEN")
# TODO: for p in nb.ipam.prefixes.filter(site="dc1", status="active"):
#           print(p.prefix, p.description)
Exercise 2
Declare a prefix with Terraform

Goal: Write a netbox_prefix resource, run plan, and confirm the change set before apply.

exercise_2
hcl
resource "netbox_prefix" "lab" {
  prefix      = "10.50.0.0/24"
  status      = "active"
  description = "exercise prefix"
}
# Then: terraform plan -out=tfplan  (review)  ->  terraform apply tfplan
Exercise 3
Catch errors with Batfish

Goal: Init a snapshot from a configs folder and run undefinedReferences(); confirm the result frame is empty.

exercise_3
python
from pybatfish.client.session import Session

bf = Session(host="localhost")
bf.set_network("lab")
bf.init_snapshot("./configs", name="snap1", overwrite=True)
# TODO: print(bf.q.undefinedReferences().answer().frame())

Curated videos & guides

Checklist โ€” pitfalls, gotchas & verification

Common pitfalls

  • โ€ขTreating live device config as the source of truth instead of NetBox.
  • โ€ขRunning terraform apply without a reviewed plan.
  • โ€ขKeeping Terraform state local on one laptop โ€” use a remote, locked backend.

Gotchas to watch

  • โ€ขNetBox API tokens are per-user and scoped โ€” a read-only token can't write.
  • โ€ขThe NetBox Terraform provider is community-maintained; pin its version.
  • โ€ขBatfish models config statically โ€” it won't catch runtime/hardware faults.

Verify it works

  • โ€ขConfirm pynetbox returns the expected objects before generating downstream config.
  • โ€ขCheck terraform plan shows only the intended additions/changes.
  • โ€ขRun Batfish undefinedReferences() and reachability questions and confirm clean results before deploy.

A mature pipeline reads intent from NetBox, renders/provisions with Terraform, and gates merges with Batfish analysis โ€” catching reachability and policy errors before deployment.