Library / SDK
Juniper/py-junos-eznc avatar
Juniper/py-junos-eznc

Junos PyEZ: automating Junos devices over NETCONF without touching XML

Python library for Junos automation

717 stars347 forksPythonApache-2.0

At a glance

What is it?
Junos PyEZ is Juniper's Python library for remote management of Junos devices, giving you tables, facts, configuration and utility functions over NETCONF so that a network engineer can work from the Python shell instead of the device CLI. It suits anyone automating Junos, and it is a Junos library that is deliberately not tied to a Junos version.
Who is it for?
Junos PyEZ fits a team automating Junos devices who wants the same operations the CLI offers in a form a program can use, and it does not fit a team automating a mixed vendor estate that needs one abstraction layer over every network operating system, since the library is specific to Junos.
Can I use it commercially?
Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 36 days ago.
What is it written in?
Mainly Python, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

Two audiences, and the promise that you need no XML knowledge

Junos PyEZ is a Python library to remotely manage and automate Junos devices, and the README states the design constraint directly: the user is not required to be a Software Programmer, to have sophisticated knowledge of Junos, or to have a complex understanding of the Junos XML API.

That constraint is what shapes the library, and the README explains it by splitting the audience in two.

For non-programmers, described as Python as a Power Shell, a network engineer can use the native Python shell on a management server as the point of control for remotely managing Junos devices. The Python shell is an interactive environment providing conditional testing, for-loops, macros and templates, and the README argues these building blocks are similar enough to other shell environments like Bash that the non-programmer can treat Python as a power-tool rather than a programming language. From there the user manages devices using native hash tables and arrays rather than device-specific Junos XML, and the README makes the contrast with the alternative explicit: resorting to screen scraping the actual Junos CLI.

For programmers, the library is described as open and extensible. The reasoning is that there is a growing interest and need to automate network infrastructure into larger IT systems, and that traditional programmers, DevOps and the wider engineering population need an abstraction library of code to support those activities. The library is designed for extensibility so a programmer can add new widgets for a specific project, with the stated benefit that there is no need to wait on the vendor to provide new functionality.

And it is not tied to a version: the README states it is not specifically tied to any version of Junos or Junos Evolved, or to any Junos or Junos Evolved product family.

The six capabilities, and what each one gives you

The feature list states the aim first: Junos PyEZ is designed to provide the same capabilities as a user would have on the Junos CLI, but in an environment built for automation tasks.

The first is remote connectivity and management of Junos devices via NETCONF. That is the transport, and it is the reason the library needs no screen scraping and no XML writing.

The second is providing facts about the device, such as software-version and serial-number. The third and fourth are retrieval operations, and they are the ones that make the library useful at scale: operational or run-state information retrieved as Tables and Views, and configuration information retrieved as Tables and Views. A Table and View is the Junos XML equivalent of a typed row and a column set, and the library presents them as Python objects.

The fifth is making configuration changes in unstructured and structured ways. Unstructured is the loose mode where you hand it a configuration snippet in a form close to what you would type, and structured is the mode where you address elements and attributes by name. Both are supported, which means a script can move between a quick change and a systematic rollout without changing tools.

The sixth is common utilities for tasks such as secure copy of files and software updates, which is the package transfer and software install path handled for you rather than left to a hand-rolled SCP call.

The whole list is best read as an inventory of the CLI's surface area. Anything you would do by logging into a device, PyEZ gives you as a function or an object, and the difference is that the return value is data rather than text.

pip, a git install, and a Docker image with a virtualenv inside it

Installation requires Python 3.8 or later and the associated pip tool, and the package on PyPI is junos-eznc.

bash
    pip install junos-eznc

Installing from Git is also supported, and the OS must have git installed. There are two forms, one for the latest master code.

bash
	pip install git+https://github.com/Juniper/py-junos-eznc.git

and one for a specific version, branch, tag or commit.

bash
	pip install git+https://github.com/Juniper/py-junos-eznc.git@<branch,tag,commit>

The Docker route is the most interesting of the three, because it is documented in two distinct modes.

In interactive mode, you move to the local directory containing your scripts and run the container, which puts you into an interactive Bash session.

bash
`docker run -it [ --rm ] [ --name pyez ] -v $PWD:/scripts juniper/pyez`

Your local scripts are mounted at /scripts. There is a wrinkle caused by PEP 668, the Python standard that marks the system environment as externally managed, and the answer is a virtual environment inside the container. The container has one installed in the /scripts folder, and you activate it with source .venv/bin/activate.

In microservice mode, the same image acts as a Python executable with the PyEZ libraries pre-installed, so you pass the script name as an argument to docker run instead of dropping into a shell.

bash
Usage: `docker run -it [ --rm ] -v some/dir:/scripts juniper/pyez [ myscript.py ]`

The microservice mode also documents how to add dependencies, either a requirements.txt passed as an argument, or OS packages for Alpine Linux given as one per line through an env var or mounted at /extras/apk.txt, with the Python equivalents through a requirements.txt at /extras/requirements.txt.

The image itself is built from python:3.12-alpine, installs the build dependencies for lxml and the XML libraries, installs the requirements and the package, then purges the compiler toolchain before shipping, so the result is a small image with the build tools removed.

The ncclient pin, and the SSH key format trap on macOS

Two notices in the README are the kind of thing that will cost an afternoon if you find them by debugging rather than by reading.

The first is the ncclient requirement. As of release 2.7.1, Junos PyEZ requires ncclient version 0.6.15 or later, and the requirements file goes further by pinning ncclient to exactly 0.7.0 with a comment explaining why: ncclient version 0.6.10 and 0.6.16 has issues with PyEZ and needs to be avoided. That is a stricter rule than the README statement, and the two together mean the installable set is narrower than 0.6.15 or later suggests.

The second is the SSH key format. When using the ssh_private_key_file argument of the Device constructor on MacOS Mojave and higher, the keys must be in RSA format rather than the newer OPENSSH format. The README gives the three commands to handle that.

bash
  - New key: `ssh-keygen -p -m PEM -f ~/.ssh/id_rsa`
bash
  - Convert an existing OPENSSH key: ``ssh-keygen -p -m PEM -f ~/.ssh/private_key`
bash
  - Check if a given key is RSA or OPENSSH format: `head -n1 ~/.ssh/private_key`

The check produces one of two outputs, RSA gives the header -----BEGIN RSA PRIVATE KEY----- and OPENSSH gives -----BEGIN OPENSSH PRIVATE KEY-----.

Both notices exist because the library is old enough to have accumulated sharp edges around the transport layer. The rest of the requirements file is unremarkable but shows what the library does: lxml for XML parsing, scp for file transfer, jinja2 for templates, PyYAML for configuration, paramiko for SSH, plus six, pyserial, yamlloader, pyparsing and transitions.

A vendor library, versioned by the vendor, and honest about the master branch

The maintenance position is stated in the first line of the README, which is unusual and useful: the repo is under active development, and if you take a clone, you are getting the latest, and perhaps not entirely stable code.

That sentence governs how you should read the release list. The recent releases are 2.8.2 on 2026-06-22, 2.8.1 on 2026-06-01 and 2.8.0 on 2026-05-19, with the last push on 2026-09-17. So the tagged releases come in a tight cluster in May and June 2026, and the master branch has been moving since. A pip install gives you the release; a git clone gives you something newer and less predictable. The README tells you this before you install, which is more than most projects do.

The packaging is thorough in a way that suggests a long-lived project with a real release process. The setup.py reads its install requirements out of requirements.txt, uses versioneer to derive the version and the release commands from the git metadata, and declares the package directory as lib with the packages found underneath it. The distribution is named junos-eznc, published under the jnpr namespace package, with the author list recorded as Jeremy Schulman, Nitin Kumar, Rick Sherman and Stacy Smith and a community contact address.

The package data configuration is worth noticing, because it explains how the operational tables work. Four subpackages ship their YAML definitions, for jnpr.junos.op, jnpr.junos.command, jnpr.junos.cfgro and jnpr.junos.resources. The op and command tables are the run-state and operational views, cfgro is configuration, and resources carries the device resource definitions. The library ships its schema with itself.

The classifiers list the project as Production/Stable, with the intended audience covering developers, information technology, system administrators and the telecommunications industry, and Python 3 support running from 3.10 through 3.15. The licence is Apache 2.0, with a separate LICENSE file and a COPYRIGHT file, and the repository also carries a SECURITY.md, a RELEASE-NOTES.md, per-distribution install guides for Ubuntu and Debian, RHEL, Fedora and CentOS, FreeBSD, macOS and Windows, a DOCKER-EXAMPLES.md, a vagrant directory, and a docs directory published on readthedocs.

Against the CLI, and against a multi-vendor automation layer

There are two comparisons worth making, and they point in opposite directions.

Against the CLI, PyEZ is not really a competitor. The stated goal is to provide the same capabilities as the CLI, in an environment built for automation. The gain is that the output is data rather than text, so a loop over fifty devices parses a table instead of running screen-scraping on fifty command outputs, and a configuration change can be structured instead of a string that has to be trusted. The cost is a dependency on NETCONF and on the ncclient library, and the fact that the device has to be reachable and willing to answer.

Against a multi-vendor automation layer, the comparison is less flattering and more honest. The library is Junos specific in its data model, its resource definitions and its configuration handling, and while it is not tied to a Junos version or product family, it is tied to Junos. A team that automates a mixed estate of Junos, IOS and NX-OS needs either a layer above PyEZ or a different library altogether, and the work of abstracting across vendors does not disappear because the Junos half is pleasant.

There is also a vendor question that a purely technical comparison misses. This is a library published under the jnpr namespace, with a vendor documentation site, a vendor forum, a vendor support address, and a vendor-maintained Docker image on the juniper organisation. That brings a real benefit, since the docs, the examples and the bug reports are the vendor's own, and a real constraint, since the pace of change is the vendor's and the abstraction layer is the vendor's design rather than something the community can reshape.

So the honest reading of the README's promise of extensibility is that you can add widgets for your project, not that you can change the shape of the library. For a team standardising on Junos, that is the right trade. For a team standardising on something else, the whole question does not arise.

Editorial conclusion

Junos PyEZ fits a team automating Junos devices who wants the same operations the CLI offers in a form a program can use, and it does not fit a team automating a mixed vendor estate that needs one abstraction layer over every network operating system, since the library is specific to Junos. Before depending on it, read the release notes for the version you pin, because the project states that a clone gives you the latest and perhaps not entirely stable code, and check the ncclient requirement, since 0.6.10 and 0.6.16 are called out as problematic and the file pins 0.7.0 exactly.

Frequently asked questions

How do I install Junos PyEZ?

Installation requires Python 3.8 or later and pip, and the package is junos-eznc. You can also install from Git with pip install git+https://github.com/Juniper/py-junos-eznc.git, using a branch, tag or commit reference for a specific version, or use the juniper/pyez Docker image.

Which ncclient version does Junos PyEZ need?

As of release 2.7.1 the library requires ncclient 0.6.15 or later, and the requirements file pins ncclient to exactly 0.7.0 because versions 0.6.10 and 0.6.16 have issues with PyEZ and need to be avoided.

What do the two Junos PyEZ Docker modes do?

In interactive mode, running the container with a volume mount of your script directory puts you into an interactive Bash session with the scripts at /scripts, and a virtual environment there must be activated because of PEP 668. In microservice mode, the same image acts as a Python executable with the library pre-installed, and you pass the script name as an argument to docker run.

Why does Junos PyEZ need a specific SSH key format on macOS?

The README states that when using the ssh_private_key_file argument of the Device constructor on MacOS Mojave and higher, the keys must be in RSA format and not the newer OPENSSH format. It gives the ssh-keygen commands to create a new key in PEM format, convert an existing OPENSSH key, and check which format a given key is in.

Official sources

  1. Juniper/py-junos-eznc on GitHub
  2. License: Apache-2.0
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/juniper-py-junos-eznc.svg)](https://hysenlabs.com/projects/juniper-py-junos-eznc)

Was this useful?