Nornir for Network Engineers
Nornir's Inventory: hosts.yaml, groups.yaml, defaults.yaml
What you'll learn
- Nornir's three-file inventory schema: hosts, groups, defaults
- How to translate the SW1-SW5 fleet into it
- Why credentials belong in none of these three files
- How custom data (like a site) attaches to a host
Listen to a guided explanation of this lesson.
Spoken narration isn't supported in this browser.
Three files, one inventory
Nornir's default inventory (the SimpleInventory plugin) reads three YAML files: hosts.yaml (who exists), groups.yaml (shared settings by group), defaults.yaml (settings applying to everything).
SW1:
hostname: 10.10.10.1
groups:
- ios_switches
data:
site: HQ
SW2:
hostname: 10.10.10.2
groups:
- ios_switches
data:
site: HQ
SW3:
hostname: 10.10.10.3
groups:
- ios_switches
data:
site: HQ
SW4:
hostname: 10.10.10.4
groups:
- ios_switches
data:
site: Branch1
SW5:
hostname: 10.10.10.5
groups:
- ios_switches
data:
site: Branch1The same SW1-SW5 fleet from every capstone since Phase 2.6 -- hostname is the management IP; groups assigns each host to ios_switches; data.site is custom data, reusing the HQ/Branch1 site grouping from Network Controllers.
ios_switches:
platform: cisco_iosA group holds settings shared by every host that lists it -- here, just the platform, the same cisco_ios string Netmiko has used since Phase 2.7.
Credentials belong in none of these three files
The tempting-but-wrong pattern
It's common to see defaults.yaml carrying username: admin / password: something. Do not do this — a credential committed to any inventory file is exactly the same mistake as hardcoding it in source code, the rule this curriculum has held since Netmiko Fundamentals.
---Genuinely empty. Credentials are injected at runtime, next.
import os
from nornir import InitNornir
nr = InitNornir(
inventory={
"plugin": "SimpleInventory",
"options": {
"host_file": "inventory/hosts.yaml",
"group_file": "inventory/groups.yaml",
"defaults_file": "inventory/defaults.yaml",
},
},
)
nr.inventory.defaults.username = os.environ["NORNIR_USERNAME"]
nr.inventory.defaults.password = os.environ["NORNIR_PASSWORD"]Credentials come from environment variables, set at runtime, and are assigned onto the already-loaded inventory's defaults -- never written into a file on disk.
SW1 username (before injection): None
SW1 password (before injection): None
SW1 username (after injection): labuser
SW1 password (after injection): labpassReal, verified output. Before injection, every host's username/password is None — genuinely absent. After injection, the default cascades to every host that doesn't override it.
Custom data: the site field
data: under a host is a free-form dict for anything the built-in schema doesn't cover — here, which site a switch belongs to.
sw4 = nr.inventory.hosts["SW4"]
print(sw4.data["site"])
print(sw4["site"]) # a shorthand for the same thingReal, verified output for both: 'Branch1'. Custom data becomes directly useful in Lesson 4, filtering hosts by exactly this field.
Key takeaways
- Nornir's SimpleInventory reads three files: hosts.yaml (who exists), groups.yaml (shared settings), defaults.yaml (settings for everything).
- The same SW1-SW5 fleet translates directly — groups assign platform via ios_switches; data.site reuses the HQ/Branch1 grouping from Network Controllers.
- Credentials belong in none of the three files — verified directly, injected at runtime from environment variables onto the loaded inventory's defaults.
- Custom data (like site) is a free-form dict under data:, directly useful for filtering in Lesson 4.