SYMPLIFY LEARNING

Upgrading Palo Alto firewalls using Ansible

In this post, I cover the upgrade of a Palo Alto firewall using an Ansible playbook.

Even though this playbook covers the upgrade of a single firewall, the real benefit comes where you have tens or hundreds of firewalls to upgrade. With the same playbook, you can upgrade all the firewalls simply by ensuring that the IP addresses of all the required firewalls are in your variables file.

First, I had to install the Palo Alto Networks Ansible collection using the command:

ansible-galaxy collection install paloaltonetworks.panos

This Ansible collection also has a number of dependencies namely:

  • pan-python
  • pan-os-python
  • xmltodict

To install these dependencies with pip, simply run the following commands:

pip install pan-python
pip install pan-os-python
pip install xmltodict

Further information about this Palo Alto Networks Ansible collection can be found in here.

With my Ansible collections and dependencies now installed, I got started with my playbook.

The first part of my playbook simply starts with defining my playbook name and specifying the variables file I want to call in the in playbook.

---
  - name: Firewall upgrade
    hosts: localhost
    gather_facts: no
    vars_files:
      - upgrade_vars.yml

The sample content of my vars file is also shown below:

firewalls:
  site_a: 172.17.30.10

credentials:
  username: "user1"
  password: "mysecurepassword"

panos_upgrade_version: "10.2.6"

Next, I move on to the actual tasks of the playbook.

My first task is to check the PAN-OS version running on the firewall.

    tasks:
      - name: Check current firewall version
        paloaltonetworks.panos.panos_facts:
          provider:
            username: "{{ credentials.username }}"
            password: "{{ credentials.password }}"
            ip_address: "{{ firewalls.site_a }}"
          gather_subset: ['system']
        register: device_info

In the above snippet, I register the full output and “store” it as a variable called ‘device_info’

Let’s see what the value of ‘device_info’ is by updating the playbook to print its value.

      - name: Print device info
        ansible.builtin.debug:
          msg: "{{device_info}}"

Now, when I run the playbook, I get the below:

The playbook runs successfully. However, I don’t need all details in the above output. I only need the value of ‘ansible_net_version’

So, I update the playbook as follows:

      - name: Print device info
        ansible.builtin.debug:
          msg: "{{device_info.ansible_facts.ansible_net_version}}"

      - name: Set fact for the current firewall PAN-OS version
        set_fact:
          current_software_version: "{{device_info.ansible_facts.ansible_net_version}}"

Now, when I run the playbook, I get the following:

Now I get just the value of the PAN-OS version, which in this case is 10.2.4.

The next portion of my playbook simply checks for the latest PAN-OS version on the firewall.

      - name: Request system software check
        paloaltonetworks.panos.panos_op:
          provider:
            username: '{{credentials.username}}'
            password: '{{credentials.password}}'
            ip_address: '{{firewalls.site_a}}'
          cmd: "request system software check"
        register: software_check_result
        until: software_check_result is not failed
        retries: 10
        delay: 30

On the Palo Alto firewall GUI, this is the equivalent of navigating to Device > Software and clicking on ‘Check Now’.

Next, I update my playbook to upgrade the PAN-OS version of the firewall if the ‘current_software_version’ (defined earlier in the playbook) is not the same as the ‘panos_upgrade_version’ I have defined in my vars.yml file.

      - name: Upgrade PAN-OS version to {{ panos_upgrade_version }} and restart
        paloaltonetworks.panos.panos_software:
          provider:
            username: '{{credentials.username}}'
            password: '{{credentials.password}}'
            ip_address: '{{firewalls.site_a}}'
          version: '{{ panos_upgrade_version }}'
          download: true
          install: true
          restart: true
        retries: 3
        delay: 120
        when: current_software_version != panos_upgrade_version
        async: 600
        poll: 0

I’ve used the async and poll parameters because of how long the firewall upgrade task may run for. Instead of having Ansible hold on to the connection until the firewall reboots, I’ve set the poll value to 0 so that Ansible starts the task and immediately moves on to the next task (even though in this case, there is no additional task to run)

After waiting a few minutes, the firewall was back up and running with the new PAN-OS version of 10.2.6.