Editing Palo Configs by Scripts: pan-os-php

There are recurring cases where tasks cannot be edited quickly and easily using the classic Palo Alto Networks GUI or Panorama. For example, editing multiple policies at once, such as during a zone migration. Or checking which policies haven’t log forwarding enabled, hence enabling it directly. Or finding unused objects, including deleting them.

For these situations (and many more!), there’s a tool with a wealth of predefined scripts: pan-os-php. This first blog post covers installation and some initial use cases.

Installation

pan-os-php is based on Docker and is maintained by Sven Waschkut. (Note that it was formerly under the hood of Palo Alto Networks itself, but is not updated anymore there.)

If you are using Windows, you can use the Windows Subsystem for Linux (WSL) as the easiest way to get it working:

Mac users can use Colima.

For the Docker part, you can follow the official documentation, which is:

Adding the current user to the Docker group to be able to start Docker without root privileges: (logout required after the following command to get it working)

By the way: It seems that Docker does not offer IPv6 support by default. What a shame! Come on, it’s 2025! Some help is here.

The installation of pan-os-php is simple: (Use the same command to update it later on.)

To start pan-os-php, always use the following command:

Basic Steps

Being in the Docker instance, everything starts with “pan-os-php” again. Press Tab two times to get some inline information. Quite useful helping keywords are: listfilters, listactions, and help, which work almost everywhere.

A very basic command uses the following three options:

  • in=filename.xml
  • type=<various-types-see-examples-below-or-inline-help>
  • out=filename-out.xml <- if not used, the default output is /dev/null ;)

1) Getting the Configuration

You can either manually download an XML file from a Palo/Panorama and place it into your working directory (which is automatically listed as the “/share” folder), or you can download it via the API from the device itself. This requires the type=upload:

Here’s a sample run:

 

After that, you can use this local XML rather than the API for every step you’re doing.

2) Do Something

Now you can work with this freshly downloaded XML file in various ways. Please have a look at the “First Use Cases” below.

A few basic keywords, though:

  • location=vysyX <- Select the vsys for a firewall (default: vsys1) or the device group for a Panorama (default: shared).
  • template=any <- Select the template if you’re within Panorama.

As a best practice, you should always do a first run with only the filter criteria (in order to see which objects will be touched), followed by a second run with the actual requested action.

3) Get the Changes

I personally prefer to extract “set” commands that I can use on the CLI on a Palo/Panorama rather than uploading a complete XML to the device itself. Fortunately, this can be done in the following way, which compares the input and output XMLs and displays appropriate “set” commands:

pan-os-php type=diff file1=pa-test1.xml file2=pa-test1-out.xml outputformatset

Alternatively, you can omit step 3 by specifying the following “outputformatset” directly within the “Do Something” part, which is: outputformatset=textfile.txt.

First Use Cases

Finding (and deleting) unused objects

For sure, everyone has unused objects such as addresses, groups, services, etc. Use pan-os-php in the following way to get rid of those objects. Hint: Yes, pan-os-php works recursively with this cool object filter: “is.unused.recursive”.

First run, just to get some information on which objects are unused, looking at the “address” and “service” objects here:

Second run to delete those objects. Note that you need the “out=” parameter for this while the 1st out is the 2nd in:

Finally, generating the “set” commands:

Now I can use those commands within the CLI to get rid of those unused objects. Just for reference, here are a few commands out of my test run:

Activating log and log forwarding on all policies

If you really want to be sure that you’re logging every policy hit, just do this. First run: on which policies is either the “log at session end” not set, or no log forwarding profile specified:

Second run, enabling “log at session end” on all of those policies and setting the log forwarding to the profile called “default”. Multiple actions in the same run can be done by separating them with a slash. This time, I’m using the appendix “outputformatset=textfile.txt” to get the required commands directly rather than setting the “out=…” parameter:

Here are a few of such commands, just for reference again. Note that only the required commands are listed by pan-os-php, that is: rules that had either the logging enabled or the log forwarding set already will only get the other required command (last two lines):

Migrating a zone to another

A slightly more complex scenario: In case you are migrating some networks from one zone to another, you want to add a second src|dst zone to all rules that currently have another zone as their src|dst.

In this example, I’m using pan-os-php with a panorama.xml file, hence specifying a location. The zone “S2S-VPN” shall get a sibling of “Transfer”. I’m doing the “from” in the first run, and the “to” in a second, while extracting the set commands from a diff between the original input and the 2nd output:

Yet another example. Replacing (rather than adding) one zone to another, in this case: “WAN” becomes “Transfer”, both for “from” and “to”. No filter needed, since only policies are involved where the current WAN zone is present. Both actions at one. One-liner:

Outlook

Please use the “listfilters” and “listactions” helpers to get ideas about what you can do with pan-os-php, or to find out which parameters the individual sections require.

Some more advanced use cases are:

  • rule analyses concerning best practices (formerly Iron Skillet)
  • firewall migration from 3rd party devices, e.g. Sophos (formerly Expedition)
  • correction of misconfigured objects

Some of those will be covered in upcoming blog posts. Stay tuned.

Photo by Mohamed Munawwar Luthfee on Unsplash.

6 thoughts on “Editing Palo Configs by Scripts: pan-os-php

  1. Hi

    Great post. This command

    “pan-os-php in=pa-test1.xml type=address ‘filter=(object is.unused.recursive)’ actions=delete out=pa-test1-out.xml” gives first all the “delete shared address “name_of_object”, then a bunch of virus, spyware and vulnerability related “set” commands that has nothing to do with type=address. I have tested it against two different Panorama XML files.

    This gives a clean output with just the “delete shared address…” commands:
    pan-os-php in=pa-test1-out.xml type=address ‘filter=(object is.unused.recursive)’ actions=delete out=pa-test1-out2.xml

    While this also includes the virus, spyware and vulnerability set commands:
    pan-os-php type=diff file1=pa-test1.xml file2=pa-test1-out2.xml outputformatset

    Very confusing and would like to know if it happens to others too.

  2. by the first usage of pan-os-php, it must be validated, if all Palo Alto Networks [PANW] default config behaviour is visible inside the XML file.

    if not, a file compare does not make sense in specific if you are working on best-practice assessments.

    PANW is NOT showing all default config within the XML file, so pan-os-php must bring in the default PAN-OS config part.

    As soon as this is done, all other parts like object cleanup, merging aso. can be directly done.

    1. Hi Sven

      Great tool you’ve created! So we have just to ignore the other “set” commands output that are generated and only use the “delete” commands? This is regarding decommission/deleting of unused objects.

      1. if you like to use the “set commands” for deleting objects, then of course you only need to focus on the “set” information.

  3. Instead of going through the whole diff process, as nicely described in this blog:
    pan-os-php type=diff file1=pa-test1.xml file2=pa-test1-out2.xml outputformatset

    Why not just use the decommission action?:
    pan-os-php in=pa-test1.xml type=address ‘filter=(object is.unused.recursive)’ actions=decommission outputformatset

    Decommission does even a better job by removing unused nested groups. Example:

    ObjectA is a member of GroupA and GroupA is a member of GroupB
    ● ObjectA and GroupA and GroupB are not referenced anywhere else in the configuration

    I tested with a service object and it removed first groupB, groupA and at the end ObjectA.

    Below is from original documentation
    ● GroupB will be removed (from documentation, outdated?)
    ● ObjectA and GroupA will remain (from documentation, outdated?)

Leave a Reply

Your email address will not be published. Required fields are marked *