Use the juniper.device.junos_package Ansible Module to Install Software
on Junos Devices
You can use the juniper.device.junos_package Ansible module to
install software on devices running Junos OS or devices running Junos OS
Evolved.
Use Ansible to Install Software
Juniper Networks provides Ansible modules that enable you to install software images
on Junos devices. Table 1 outlines
the modules. If you are already using a given set of modules from the
juniper.device collection, use the module for that set.
|
Collection |
Module Set |
Module Name |
|---|---|---|
|
|
|
|
|
|
The following sections discuss how to use the
juniper.device.junos_package module to install software on
Junos devices. The sections outline how to specify the software image location, the
general software installation process and module options, and how to perform more
specialized upgrade scenarios on devices that support these features.
How to Specify the Software Image Location
When you use the juniper.device.junos_package module to install
software on Junos devices, you can download the software package to the Ansible
control node. The module, by default, copies the package to the target device
before performing the installation. You can also instruct the module to install
a software image that already resides on the target device.
Table 2 outlines the module arguments that you must set depending on the software
package location. The module must include the src argument with
the path to the software image. The no_copy argument defaults
to false, which instructs the module to copy the software
package from the Ansible control node to the target device's
/var/tmp directory. If the software package already
resides on the target device, the module's arguments must include
no_copy: true.
|
Software Package Location |
|
|
|---|---|---|
|
Ansible control node |
Omit or set to |
File path, including the filename, of the software package on the local control node. File paths are relative to the playbook directory. |
|
Target device |
Set to |
File path on the target device where the software package must already reside. The default directory is /var/tmp. |
Providing Connection and Authentication Parameters for the Module
The juniper.device.junos_package module uses the Junos PyEZ
library to perform software installations. Because of this dependency, the
module does not use the default connection and authentication parameters to
connect to Junos devices.
Unlike other juniper.device.junos_* modules, the
juniper.device.junos_package module requires that you pass
in certain connection and authentication parameters as module arguments. At a
minimum, you must include a provider dictionary as a top-level
module argument and define the host variable. The dictionary
can also specify the username, password, and
port variables.
For example, the provider dictionary can include the following
keys.
provider: host: host username: username password: password port: port
If you do not define a provider dictionary with the
host variable, your playbook might generate the following
error:
[ERROR]: Task failed: You must provide either 'host' or 'sock_fd' value
For the juniper.device.junos_package module, you can also
specify some connection parameters, such as the user, in your SSH config file.
By default, the module queries the default SSH configuration file at
~/.ssh/config and uses SSH keys loaded in the SSH key
agent or in the default location. To specify the path to a non-default SSH
configuration file, include the module's ssh_config argument .
To use a non-default identify file, set the module's
ssh_private_key_file argument to the path of the SSH
private key file.
Installation Process Overview
You can use the juniper.device.junos_package module to install a
software package on Junos devices. By default, the
junos_package module validates the new package against the
current configuration, installs the package on both Routing Engines, if
applicable, and reboots both Routing Engines. To use the module to install a
software package, provide the source package location, provider
dictionary, and any required arguments. For the complete list of available
options, see the module documentation.
When you execute the juniper.device.junos_package module, it
performs the following operations:
Once the software package is on the target device, whether downloaded there initially or copied over by the module, the module then performs the following operations:
Validates the new package against the current configuration unless you set
validatetofalse.Installs the package on each individual Routing Engine.
Reboots each upgraded Routing Engine, unless the
rebootargument is set tofalse.
For example, the following playbook installs a software package that resides on the local Ansible control node:
---
- name: Install software with juniper.device.junos_package
hosts: junos
gather_facts: false
tasks:
- name: Install Junos OS package
juniper.device.junos_package:
provider:
host: "{{ ansible_host }}"
username: "{{ ansible_user }}"
src: "software/junos-install-mx-x86-64-24.2R2.18.tgz"
unlink: true
register: response
- name: Print the response
ansible.builtin.debug:
var: response
Similarly, the following playbook installs a software package that resides in the /var/tmp directory on the target device.
---
- name: Install software with juniper.device.junos_package
hosts: junos
gather_facts: false
vars:
version: "25.2R1.9"
package: "junos-install-mx-x86-64-25.2R1.9.tgz"
tasks:
- name: Install Junos OS package
juniper.device.junos_package:
no_copy: true
provider:
host: "{{ ansible_host }}"
username: "{{ ansible_user }}"
reboot: true
src: "{{ package }}"
version: "{{ version }}"
register: response
- name: Print response
ansible.builtin.debug:
var: responseHow to Perform a Unified ISSU
The unified ISSU feature enables you to upgrade between two different Junos OS releases with no disruption on the control plane and with minimal disruption of traffic. For more information about the unified ISSU feature, see the software documentation for your product.
The juniper.device.junos_package module supports performing a
unified ISSU on devices that support the feature and meet the necessary
requirements. To perform a unified ISSU, the
juniper.device.junos_package module must include the
issu: true argument. For example:
---
- name: Perform a Junos OS software upgrade
hosts: mx1
gather_facts: no
tasks:
- name: Perform a unified ISSU
juniper.device.junos_package:
issu: true
provider:
host: "{{ ansible_host }}"
src: "junos-install-mx-x86-64-24.4R2.16.tgz"
register: response
- name: Print the response
ansible.builtin.debug:
var: response
Example: Use Ansible to Install Software
This example uses the juniper.device.junos_package module to install
a software image on a Junos device.
- Requirements
- Overview
- Create the Playbook
- Execute the Playbook
- Verification
- Troubleshoot Connection Errors
Requirements
This example uses the following hardware and software components:
-
Configuration management server running Ansible 2.17 or later with the
juniper.devicecollection installed -
Junos device with NETCONF enabled and a user account configured with appropriate permissions
-
SSH public/private keypair configured for the appropriate user on the Ansible control node and Junos device
-
Existing Ansible inventory file that defines the required hosts and their network connection and network OS variables
Overview
This example presents an Ansible playbook that uses the
juniper.device.junos_package module to upgrade Junos OS on
the hosts in the specified inventory group. In this example, the software image
resides on the Ansible control node, and the module copies the image to the
target device before installing it. The
juniper.device.junos_package module requires that you
explicitly define the host variable. The module's
provider dictionary defines host and
username.
The playbook first defines two tasks to verify if the software image exists and
to stop execution if the file doesn't exist. The Check NETCONF
connectivity task utilizes the
ansible.builtin.wait_for module to try to establish a
NETCONF session with the Junos device using the default NETCONF port 830. If the
control node fails to establish a NETCONF session with a device during playbook
execution, then it skips the remaining tasks in the play for that device.
The Install Junos OS package task executes the
juniper.device.junos_package module provided that the
NETCONF check was successful. The version argument defines the
desired Junos OS version as it would be reported by the show
version command on the Junos device. During playbook execution, the
module first checks that the requested version is not already installed on the
device. If the requested version is different from the installed version, the
module installs the requested version.
The src argument defines the path of the software package on the
Ansible control node. During the installation, the module:
-
Performs a storage cleanup operation on the target device
-
Copies the software image to the /var/tmp directory on the device
-
Verifies the file’s checksum
-
Validates the new software against the active configuration
-
Installs the software on each Routing Engine on the target host
-
Reboots each Routing Engine on the target host
By default, the juniper.device.junos_package module reboots each
Routing Engine after the installation completes; however, this task explicitly
sets reboot: true for clarity. The task stores the module
result in the response variable. The example executes the
playbook with the -v option to log messages of severity level
INFO or higher in the Ansible log to monitor the installation.
Create the Playbook
Create the Ansible Playbook
To create a playbook that uses the
juniper.device.junos_package module to install a
software image on a Junos device:
-
Include the boilerplate for the playbook and this play.
--- - name: Install software with juniper.device.junos_package hosts: junos gather_facts: false
-
Define or import any necessary variables, which for this example, includes the desired Junos OS version, the path to the new image, and the NETCONF port.
vars: version: "25.2R1.9" package: "software/junos-install-mx-x86-64-25.2R1.9.tgz" netconf_port: 830 Create tasks to verify if the software package exists and stop execution if the package does not exist.
tasks: - name: Check if the local package file exists ansible.builtin.stat: path: "{{ package }}" register: stat_result - name: Stop if image not found ansible.builtin.fail: msg: "Image not found on the control node: {{ package }}." when: not stat_result.stat.exists-
(Optional) Create a task to verify NETCONF connectivity.
- name: Check NETCONF connectivity ansible.builtin.wait_for: host: "{{ ansible_host }}" port: "{{ netconf_port }}" timeout: 5 -
Create the task to install the Junos OS package on the device.
- name: Install Junos OS package juniper.device.junos_package: no_copy: false provider: host: "{{ ansible_host }}" username: "{{ ansible_user }}" reboot: true src: "{{ package }}" version: "{{ version }}" register: response -
(Optional) Create a task to print the module response.
- name: Print response ansible.builtin.debug: var: response
Results
On the Ansible control node, review the completed playbook. If the playbook does not display the intended code, repeat the instructions in this example to correct the playbook.
---
- name: Install software with juniper.device.junos_package
hosts: junos
gather_facts: false
vars:
version: "25.2R1.9"
package: "software/junos-install-mx-x86-64-25.2R1.9.tgz"
netconf_port: 830
tasks:
- name: Check if the local package file exists
ansible.builtin.stat:
path: "{{ package }}"
register: stat_result
- name: Stop if image not found
ansible.builtin.fail:
msg: "Image not found on the control node: {{ package }}."
when: not stat_result.stat.exists
- name: Check NETCONF connectivity
ansible.builtin.wait_for:
host: "{{ ansible_host }}"
port: "{{ netconf_port }}"
timeout: 5
- name: Install Junos OS package
juniper.device.junos_package:
no_copy: false
provider:
host: "{{ ansible_host }}"
username: "{{ ansible_user }}"
reboot: true
src: "{{ package }}"
version: "{{ version }}"
register: response
- name: Print response
ansible.builtin.debug:
var: response
Execute the Playbook
The playbook in this example uses the following Ansible inventory file. To simplify the execution, the Ansible inventory group includes only one host.
[junos] mx1.example.com ansible_host=198.51.100.101 [junos:vars] ansible_connection=ansible.netcommon.netconf ansible_network_os=juniper.device.junos ansible_user=ansible-user
To execute the playbook:
-
Issue the
ansible-playbookcommand on the control node, and provide the playbook path and any required options.ansible-user@ansible-cn:~/ansible$ ansible-playbook ansible-pb-junos-package-install-os.yaml -v Using /home/ansible-user/ansible/ansible.cfg as config file PLAY [Install software with juniper.device.junos_package] ************************** TASK [Check if the local package file exists] ************************************** ok: [mx1.example.com] => {"changed": false, "stat": {"atime": 1772040785.405, "attr_flags": "e", "attributes": ["extents"], "block_size": 4096, "blocks": 6831952, "charset": "binary", "checksum": "81510a5928de8f9244c4dbc6f1f6eccc15bce36a", "ctime": 1772040778.1, "dev": 64512, "device_type": 0, "executable": false, "exists": true, "gid": 1000, "gr_name": "ansible-user", "inode": 1606049, "isblk": false, "ischr": false, "isdir": false, "isfifo": false, "isgid": false, "islnk": false, "isreg": true, "issock": false, "isuid": false, "mimetype": "application/gzip", "mode": "0664", "mtime": 1771902809.953, "nlink": 1, "path": "software/junos-install-mx-x86-64-25.2R1.9.tgz", "pw_name": "ansible-user", "readable": true, "rgrp": true, "roth": true, "rusr": true, "size": 3497954709, "uid": 1000, "version": "783053065", "wgrp": true, "woth": false, "writeable": true, "wusr": true, "xgrp": false, "xoth": false, "xusr": false}} TASK [Stop if image not found] ***************************************************** skipping: [mx1.example.com] => {"changed": false, "false_condition": "not stat_result.stat.exists", "skip_reason": "Conditional result was False"} TASK [Check NETCONF connectivity] ************************************************** ok: [mx1.example.com] => {"changed": false, "elapsed": 0, "match_groupdict": {}, "match_groups": [], "path": null, "port": 830, "search_regex": null, "state": "started"} TASK [Install Junos OS package] **************************************************** changed: [mx1.example.com] => {"changed": true} TASK [Print response] ************************************************************** ok: [mx1.example.com] => { "response": { "changed": true, "failed": false } } PLAY RECAP ************************************************************************* mx1.example.com : ok=4 changed=1 unreachable=0 failed=0 skipped=1 rescued=0 ignored=0
Verification
Purpose
Verify that the software installation was successful.
Action
The playbook output should indicate any failed tasks. However, you can also review the contents of the Ansible log file for additional details. Sample log file output is shown here. Some output has been omitted for brevity.
2026-02-25 13:47:12,167 p=22589 u=ansible-user n=ansible INFO| PLAY [Install software with juniper.device.junos_package] **************************
2026-02-25 13:47:12,177 p=22589 u=ansible-user n=ansible INFO| TASK [Check if the local package file exists] **************************************
2026-02-25 13:47:13,500 p=22589 u=ansible-user n=ansible INFO| network_os is set to juniper.device.junos
...
2026-02-25 13:47:49,822 p=22589 u=ansible-user n=ansible INFO| ok: [mx1.example.com] => {"changed": false, "stat": {"atime": 1772040785.405, "attr_flags": "e", "attributes": ["extents"], "block_size": 4096, "blocks": 6831952, "charset": "binary", "checksum": "81510a5928de8f9244c4dbc6f1f6eccc15bce36a", "ctime": 1772040778.1, "dev": 64512, "device_type": 0, "executable": false, "exists": true, "gid": 1000, "gr_name": "ansible-user", "inode": 1606049, "isblk": false, "ischr": false, "isdir": false, "isfifo": false, "isgid": false, "islnk": false, "isreg": true, "issock": false, "isuid": false, "mimetype": "application/gzip", "mode": "0664", "mtime": 1771902809.953, "nlink": 1, "path": "software/junos-install-mx-x86-64-25.2R1.9.tgz", "pw_name": "ansible-user", "readable": true, "rgrp": true, "roth": true, "rusr": true, "size": 3497954709, "uid": 1000, "version": "783053065", "wgrp": true, "woth": false, "writeable": true, "wusr": true, "xgrp": false, "xoth": false, "xusr": false}}
2026-02-25 13:47:49,827 p=22589 u=ansible-user n=ansible INFO| TASK [Stop if image not found] *****************************************************
2026-02-25 13:47:49,858 p=22589 u=ansible-user n=ansible INFO| skipping: [mx1.example.com] => {"changed": false, "false_condition": "not stat_result.stat.exists", "skip_reason": "Conditional result was False"}
2026-02-25 13:47:49,863 p=22589 u=ansible-user n=ansible INFO| TASK [Check NETCONF connectivity] **************************************************
2026-02-25 13:47:51,589 p=22589 u=ansible-user n=ansible INFO| network_os is set to juniper.device.junos
2026-02-25 13:47:52,233 p=22589 u=ansible-user n=ansible INFO| ok: [mx1.example.com] => {"changed": false, "elapsed": 0, "match_groupdict": {}, "match_groups": [], "path": null, "port": 830, "search_regex": null, "state": "started"}
2026-02-25 13:47:52,240 p=22589 u=ansible-user n=ansible INFO| TASK [Install Junos OS package] ****************************************************
2026-02-25 13:47:53,835 p=22654 u=ansible-user n=ncclient.transport.ssh INFO| Connected (version 2.0, client OpenSSH_9.7)
2026-02-25 13:47:54,054 p=22654 u=ansible-user n=ncclient.transport.ssh INFO| Authentication (publickey) successful!
...
2026-02-25 13:47:55,153 p=22654 u=ansible-user n=ncclient.transport.ssh INFO| [host 198.51.100.101 session-id 19039] Sending:
b'<?xml version="1.0" encoding="UTF-8"?><nc:rpc xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="urn:uuid:fb21698c-a6c7-4298-99d5-e6a35c847e53"><command>show version invoke-on all-routing-engines</command></nc:rpc>]]>]]>'
...
b'<?xml version="1.0" encoding="UTF-8"?><nc:rpc xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="urn:uuid:50b98bb4-2cd7-4b36-be29-34abc2a9b28e"><request-system-storage-cleanup/></nc:rpc>]]>]]>'
...
b'<?xml version="1.0" encoding="UTF-8"?><nc:rpc xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="urn:uuid:1de8055f-1c2a-4593-8718-b41e125ccb7e"><get-checksum-information><path>/var/tmp/junos-install-mx-x86-64-25.2R1.9.tgz</path></get-checksum-information></nc:rpc>]]>]]>'
2026-02-25 13:48:34,119 p=22654 u=ansible-user n=ncclient.transport.ssh INFO| [host 198.51.100.101 session-id 19039] Received message from host
2026-02-25 13:48:34,197 p=22654 u=ansible-user n=paramiko.transport INFO| Connected (version 2.0, client OpenSSH_9.7)
2026-02-25 13:48:34,508 p=22654 u=ansible-user n=paramiko.transport INFO| Authentication (publickey) successful!
...
b'<?xml version="1.0" encoding="UTF-8"?><nc:rpc xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="urn:uuid:27f171f1-b41b-4800-af86-7c7190b30b5f"><get-checksum-information><path>/var/tmp/junos-install-mx-x86-64-25.2R1.9.tgz</path></get-checksum-information></nc:rpc>]]>]]>'
...
b'<?xml version="1.0" encoding="UTF-8"?><nc:rpc xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="urn:uuid:a0bff79a-3bc8-40d1-864c-1c0bef760947"><request-package-validate><package-name>/var/tmp/junos-install-mx-x86-64-25.2R1.9.tgz</package-name></request-package-validate></nc:rpc>]]>]]>'
...
b'<?xml version="1.0" encoding="UTF-8"?><nc:rpc xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="urn:uuid:189db69e-3df5-49dd-9edc-a6647baf9d67"><request-package-add><no-validate/><package-name>/var/tmp/junos-install-mx-x86-64-25.2R1.9.tgz</package-name><re0/></request-package-add></nc:rpc>]]>]]>'
...
b'<?xml version="1.0" encoding="UTF-8"?><nc:rpc xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="urn:uuid:a75f1003-0d93-49dc-9061-c2bb63fb6a00"><request-package-add><no-validate/><package-name>/var/tmp/junos-install-mx-x86-64-25.2R1.9.tgz</package-name><re1/></request-package-add></nc:rpc>]]>]]>'
...
b'<?xml version="1.0" encoding="UTF-8"?><nc:rpc xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="urn:uuid:45076019-5995-4998-81b8-779d4332a03d"><request-reboot><both-routing-engines/><in>0</in></request-reboot></nc:rpc>]]>]]>'
2026-02-25 13:58:15,278 p=22654 u=ansible-user n=ncclient.transport.ssh INFO| [host 198.51.100.101 session-id 19039] Received message from host
2026-02-25 13:58:15,296 p=22589 u=ansible-user n=ansible INFO| changed: [mx1.example.com] => {"changed": true}
2026-02-25 13:58:15,303 p=22589 u=ansible-user n=ansible INFO| TASK [Print response] **************************************************************
2026-02-25 13:58:15,486 p=22589 u=ansible-user n=ansible INFO| ok: [mx1.example.com] => {
"response": {
"changed": true,
"failed": false
}
}
2026-02-25 13:58:15,488 p=22589 u=ansible-user n=ansible INFO| PLAY RECAP *************************************************************************
2026-02-25 13:58:15,489 p=22589 u=ansible-user n=ansible INFO| mx1.example.com : ok=4 changed=1 unreachable=0 failed=0 skipped=1 rescued=0 ignored=0
Meaning
The log file contents indicate that the playbook successfully copied and installed the image on both Routing Engines on the target device.
Troubleshoot Connection Errors
Problem
The playbook generates an error message for a missing host
value.
[ERROR]: Task failed: You must provide either 'host' or 'sock_fd' value
Solution
The juniper.device.junos_package module uses the Junos PyEZ
library to handle the software installation operations. You must define the
host variable to connect to the managed device.
To specify the host variable, define a provider dictionary
as a module argument. Specify host and any other required
variables that you have not supplied in an SSH configuration file. For
example:
juniper.device.junos_package:
provider:
host: "{{ ansible_host }}"
username: "{{ ansible_user }}"
...