It took me awhile to get “the point” of Nornir. Was it just a Python library that helps you script? Was it a configuration management tool that competes with the likes of Salt and Ansible?

Turns out it’s not really any of the above. Nornir is an automation framework, as they like to call it. If I’m being completely honest, I don’t think that’s a correct term – “framework.” I really think it’s an automation design pattern. 

Now don’t worry – I’m not going to bore you to death. I just think this is an important distinction (or rather comparison) to make.

Think about the MVC design pattern for a moment. If you’re not familiar with MVC, don’t worry. Software that was created with an MVC design pattern is, in other terms, software that is broken down into three pieces. The Model is the shape data will take (typically defined by classes). The View is what end users will see and interact with once the Model has been rendered on the website. The Controller is the brains of the operation that manipulates and validates data as it is modeled (the M) into a View (V), 

Think about the last time you filled out a basic form on a website. You typed in all your data, and clicked Submit. The submit button was the Controller, which retrieved the data from fields and placed them into the Model so that it could do something with the data, like render a confirmation page (the View).

Aren’t we here to talk about network automation? Yep. And now we will. Similar to how MVC breaks an application apart into three core pieces, so does Nornir, though it doesn’t behave exactly like MVC.

 Let’s get this straight – the point of Nornir is to write code once that gets applied to all of your devices simultaneously. You choose how exactly this configuration happens – details below.

First, and maybe most importantly, is the separation of Inventory. This was actually one of the things that attracted me most to Ansible at first, and it works almost identically in Nornir. You define your inventory, host variables, group variables, and a set of default variables. This is important because your code never has to worry about inventory (unless you want to explicitly target a subset of the inventory, which is easy to do in Nornir.

Second, you choose the transport mechanism. And I mean that literally. You’ll choose which library you want to carry out your task against all of your inventory. So maybe you want to use Netmiko, or Scrapli, or Scrapli_Netconf, or NAPALM, or even PyEZ (which I wrote and deployed this week). For the record, these are called Connection Plugins. One of the biggest reasons you choose to go with Nornir is because it is pluggable – contributors are creating new plugins to extend how Nornir does what it is does all the time.

Third, and last, you write the code that will be applied to all devices in the inventory and transported using your transport. Here’s how it looks when you bring it all together. An excerpt from my PyEZ Plugin:

from nornir_pyez.plugins.tasks import pyez_int_terse
import os
from nornir import InitNornir
from nornir_utils.plugins.functions import print_result
from rich import print

script_dir = os.path.dirname(os.path.realpath(__file__))

nr = InitNornir(config_file=f"{script_dir}/config.yml")

response =

# response is an AggregatedResult, which behaves like a list
# there is a response object for each device in inventory
devices = []
for dev in response:

The goal of the above code is to run “show interfaces terse” on a group of Juniper devices. This is the equivalent of “show ip int br” on Cisco devices.

First, the above code makes virtually no mention of hostnames, IP address, or credentials. That is because that all exists in inventory and groups files. Secondly, I’ve chosen to import the explicit function called “pyez_int_terse” from the nornir_pyez plugin. The plugin handles communicating with the inventory files and all of the ssh connection. The function itself will issue the correct commands (in this case the show_interfaces_terse RPC). Lastly, you will see the run() method with a task=FunctionNameHere. This is what tells Nornir, run the function as a task against every device in the inventory simultaneously. Point is, I wrote one function without specifying any inventory or credentials. The function is executed using the transport library that I chose, simultaneously, against the devices.

It’s ridiculously powerful once you “get” it. But it does take a few runs to get the hang of it. I highly recommend watching John McGovern’s content on CBT Nuggets if you are serious at all about exploring this tool.

Ahem. You need to watch that content. But always remember, practice makes perfect!