Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 13 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,10 @@ This repository would be of interest to you if:

### Requirements

* 🐍 Python 3.11+
* Python 3.11+
* Ansible 2.16+

* Panorama (this collection does NOT work for standalone firewalls or Strata Cloud Manager)
* NGFWs connected to Panorama must be running Routed mode

### Install this collection

Expand Down Expand Up @@ -137,4 +138,13 @@ ansible-playbook your_playbook.yml -i inventory.yml --extra-vars=@vars_file.yml

## Need help?

Read the [docs](https://paloaltonetworks.github.io/ansible_panos_policy_orchestration/) for more information.
Read the [docs](https://paloaltonetworks.github.io/ansible_panos_policy_orchestration/) for more information.

## Responsible AI Assistance Disclosure

Generative AI through the use of large language models has been used selectively in this repository
in the following ways:

1. Creating or editing documentation
2. Refactoring modules
3. Creation of unit tests
114 changes: 96 additions & 18 deletions docs/index.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,12 @@
# Welcome the PAN-OS Policy Orchestration (with Ansible!) docs
# Ansible PAN-OS Policy Automation

![GitHub commit activity](https://img.shields.io/github/commit-activity/w/adambaumeister/ansible_panos_policy_orchestration)
![GitHub commits difference between two branches/tags/commits](https://img.shields.io/github/commits-difference/adambaumeister/ansible_panos_policy_orchestration?base=master&head=develop&label=Changes%20Pending%20Release)
![GitHub Actions Workflow Status](https://img.shields.io/github/actions/workflow/status/adambaumeister/ansible_panos_policy_orchestration/ci.yml)
![GitHub License](https://img.shields.io/github/license/adambaumeister/ansible_panos_policy_orchestration)
![GitHub Repo stars](https://img.shields.io/github/stars/adambaumeister/ansible_panos_policy_orchestration)
![GitHub Release](https://img.shields.io/github/v/release/adambaumeister/ansible_panos_policy_orchestration)
![Github Pages](https://img.shields.io/badge/github-pages-black?logo=githubpages&link=https%3A%2F%2Fadambaumeister.github.io%2Fansible_panos_policy_orchestration%2F)

This repository provides a framework and a philosophy for creating PAN-OS security policies
via Automation.
Expand All @@ -13,27 +21,21 @@ This repository would be of interest to you if:

### Requirements

* 🐍 Python 3.11+
* Python 3.11+
* Ansible 2.16+
* Panorama (this collection does NOT work for standalone firewalls or Strata Cloud Manager)
* NGFWs connected to Panorama must be running Routed mode


### Install the Paloaltonetworks Collection
### Install this collection

```shell
ansible-galaxy install paloaltonetworks.panos
```

### Clone this repo

```shell
# ssh
git clone git@github.com:adambaumeister/ansible_panos_policy_orchestration.git
# https
https://github.com/adambaumeister/ansible_panos_policy_orchestration.git
ansible-galaxy install paloaltonetworks.panos_policy_automation
```

### Define your Inventory

In this example, we are defining one panorama host under "lab".

```yaml title='inventory.yml'
all:
children:
Expand All @@ -49,7 +51,7 @@ all:
# Username should be provided via PAN_USERNAME environment variable
# Example: export PAN_USERNAME="admin"
vars:
# Common variables for lab environment
# Common variables
ansible_connection: local
ansible_python_interpreter: "{{ ansible_playbook_python }}"
# These variables are only used when creating COMPLETELY NEW policies
Expand All @@ -59,10 +61,86 @@ all:
default_rule_location: bottom
```

### Run the connectivity playbook to validate connectivity
### Define your preset policy files

Preset policy files are used to map incoming policy requests to object groups or security rules using Ansible filter
logic. Policy files are literal task files that produce the required variables for the role to execute.

Here's an example:

```yaml
---
# This is the Webservers outbound policy. The purpose of these tasks is to take incoming requests and see if they
# match this policy, returning the preset address group that they can be added to when a policy change is required.
# Webservers should be allowed to talk to any host on the internet, so we can disregard the destination IP!

- name: Match webserver outbound policy
ansible.builtin.set_fact:
policy_match: true # Set the fact that we did match a policy
policy_creation_source_address_group: PRESET_LAB_WEB_OUTBOUND # In this case, the policy preset is an address_group type
application_group: PRESET_LAB_WEB_OUTBOUND # If an application is passed, we should also include it in the policy.
device_group: Lab # Finally, we set the device group!
when:
- policy_creation_source_ip is defined
- policy_creation_destination_ip is defined
- "'10.10.10.0/24' | ansible.utils.network_in_network( policy_creation_source_ip )"
- "not '10.0.0.0/8' | ansible.utils.network_in_network( policy_creation_destination_ip )"
```

### Create the policy request, as an ansibles vars file

Now, we have to give Ansible variables for the new policy to define the attributes.

For simplicity, we do so within a vars file.

```yaml
---
policy_creation_source_ip: 10.10.10.11
policy_creation_destination_ip: 8.8.8.8
application: dns
policy_creation_policy_files:
- example_outbound_policy_file.yml # <---- Note we included your "policy file" here!
```

### Create your playbook and include the role

```yaml
---
- hosts: lab # <---- Replace this with your group
connection: local
gather_facts: false
name: Test the Lookup Policy playbook

vars:
provider:
ip_address: "{{ ansible_host }}"
username: "{{ lookup('env', 'PAN_USERNAME') }}"
password: "{{ lookup('env', 'PAN_PASSWORD') }}"

roles:
- paloaltonetworks.panos_policy_automation.policy_creation # Note the included role

tasks:
- name: Print the results
ansible.builtin.debug:
msg: "{{ policy_creation_security_policy_match_result }}"
```

### Execute the playbook

Note, replace the playbook and vars file names with your versions.

```shell
ansible-playbook playbooks/testing/connectivity.yml
ansible-playbook your_playbook.yml -i inventory.yml --extra-vars=@vars_file.yml
```

[Proceed to the User Guide](user_guide/introduction.md)
## Need help?

Read the [docs](https://paloaltonetworks.github.io/ansible_panos_policy_orchestration/) for more information.

## Responsible AI Assistance Disclosure

Generative AI through the use of large language models has been used selectively in this repository
in the following ways:

1. Creating or editing documentation
195 changes: 195 additions & 0 deletions docs/reference/add_address_to_preset_group.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
# add_address_to_preset_group.yml

**Location:** `roles/policy_creation/tasks/preset/add_address_to_preset_group.yml`

## Purpose
Creates an address object for a given IP address and adds it to an existing address group. This is used when traffic matches a preset policy that uses address groups, allowing new addresses to be added to existing policies without creating entirely new rules.

## What it does
1. **Generates object name** - Creates a standardized name for the address object
2. **Creates address object** - Creates the address object in the specified device group
3. **Retrieves existing group** - Gets the current members of the target address group
4. **Updates address group** - Adds the new address object to the group (if not already present)
5. **Displays results** - Shows the operation status

## Execution Flow

```mermaid
flowchart TD
A[Generate address object name<br/>addr_IP_sanitized] --> B[Create ADDRESS object<br/>in specified device group]
B --> C[Get existing ADDRESS GROUP<br/>gather current members]
C --> D{Object already<br/>in group?}
D -->|No| E[Add to group<br/>prepend to list]
D -->|Yes| F[Display results]
E --> F
```

## Required Variables

| Variable | Description |
|----------|-------------|
| `_address` | The IP address or CIDR to add (passed when including this task) |
| `_address_group` | The name of the address group to update (passed when including this task) |
| `policy_creation_device_group` | The device group where objects are created |
| `provider` | PAN-OS connection details (ip_address, username, password) |

## Generated Variables

| Variable | Format | Description |
|----------|--------|-------------|
| `policy_creation_created_object_name` | `addr_<ip_sanitized>` | The name of the created address object |
| `policy_creation_address_creation` | dict | Result from address object creation |
| `policy_creation_existing_group` | dict | Gathered state of the address group |
| `policy_creation_group_addition` | dict | Result from adding object to group |

## Address Object Naming

IP addresses are sanitized for object names by:
- Replacing `.` with `_`
- Replacing `/` with `_`

Examples:
- `10.1.1.5` → `addr_10_1_1_5`
- `192.168.1.0/24` → `addr_192_168_1_0_24`

## Address Object Properties

Created objects have:
- **Type**: `ip-netmask`
- **Device Group**: From `policy_creation_device_group` variable
- **Description**: `"Auto-created address object for <ip_address>"`
- **State**: `present`

## Address Group Update Behavior

### Gathering Existing Members
The task uses `state: gathered` to retrieve the current address group configuration:
```yaml
panos_address_group:
state: gathered
```

This returns the existing `static_value` list of member objects.

### Adding New Member
The new object is **prepended** to the existing list:
```yaml
static_value: "{{ [new_object] + existing_objects }}"
```

This places the new address object at the beginning of the group's member list.

### Conditional Update
The group is only updated when:
```yaml
when: policy_creation_created_object_name not in policy_creation_existing_group.gathered.static_value
```

This prevents duplicate additions and ensures idempotency.

## Dependencies

- Requires PAN-OS collection (`paloaltonetworks.panos`)
- Requires the following modules:
- `panos_address_object`
- `panos_address_group`
- Address group must already exist in the device group

## Usage Context

This file is included from `main.yml` when preset policies match:

```yaml
- name: ADDRESS GROUP PRESET - Deploy the Source IP to policy based on preset configuration
ansible.builtin.include_tasks:
file: preset/add_address_to_preset_group.yml
vars:
_address: "{{ policy_creation_source_ip }}"
_address_group: "{{ policy_creation_source_address_group }}"
when:
- policy_creation_source_address_group is defined
```

And similarly for destination IPs:

```yaml
- name: ADDRESS GROUP PRESET - Deploy the Destination IP to policy based on preset configuration
ansible.builtin.include_tasks:
file: preset/add_address_to_preset_group.yml
vars:
_address: "{{ policy_creation_destination_ip }}"
_address_group: "{{ policy_creation_destination_address_group }}"
when:
- policy_creation_destination_address_group is defined
```

## Preset Policy Integration

For this task to be triggered, a preset policy task file must:

1. Set `policy_creation_policy_match: true`
2. Define the target address group variable:
- `policy_creation_source_address_group` for source addresses
- `policy_creation_destination_address_group` for destination addresses

Example preset policy task:
```yaml
- name: Check if this matches webserver outbound policy
ansible.builtin.set_fact:
policy_creation_policy_match: true
policy_creation_source_address_group: "webserver-sources"
when:
- policy_creation_source_ip is match("10.50.*")
```

## Output Display

The debug output shows:
```
Address object: addr_10_1_1_5
Added to group: webserver-sources
Device group: Production
Status: SUCCESS
```

Status is determined by whether `policy_creation_group_addition` succeeded.

## Idempotency

The task is idempotent because:
- Address object creation uses `state: present` (won't fail if exists)
- Group update only happens if object isn't already a member
- Re-running with the same parameters makes no changes

## Important Notes

### Prerequisites
- The address group must exist before running this task
- The device group must exist and be accessible
- The calling user must have permissions to modify objects

### Group Membership Order
- New members are prepended to the group
- This may affect group member ordering if that's significant to your policies

### No Validation
- The task doesn't validate that the IP address is well-formed
- PAN-OS will reject invalid IP addresses during object creation

## Example Scenario

Given:
- Address: `10.1.1.5/32`
- Address Group: `prod-webservers`
- Device Group: `Production`
- Existing group members: `['addr_10_1_1_1', 'addr_10_1_1_2']`

Result:
- Creates object: `addr_10_1_1_5_32`
- Updated group members: `['addr_10_1_1_5_32', 'addr_10_1_1_1', 'addr_10_1_1_2']`

## Related Files

- [add_application_to_preset_group.md](add_application_to_preset_group.md) - Similar workflow for applications
- [add_url_to_preset_category.md](add_url_to_preset_category.md) - Similar workflow for URLs
- [User Guide: Preset Policies](../user_guide/preset_policy.md) - How to create preset policy files
Loading