PaloAltoNetworks/pan-os-ansible

Trying to disable an existing security rule - rule application and destination being changed incorrectly

david-ramsden opened this issue · 2 comments

Describe the bug

When trying to update an existing security rule, to set it to disabled and add a mandatory audit comment, the following error is returned from Panorama:

Failed update application: application is invalid. 'any' should not be used with another application

After the above error is returned, Panorama does show a change summary, which is not as expected.

Expected behavior

The security rule is disabled without any errors or other modifications.

Current behavior

  1. Error returned from API (see above).
  2. When reviewing the change summary in Panorama, the rule is not disabled but other properties have been modified or added to the rule (see screenshot).

Possible solution

Unknown.

Steps to reproduce

Snippet from playbook:

    - name: Disable
      paloaltonetworks.panos.panos_security_rule:
        provider: "{{ provider }}"
        rule_name: "{{ rule['Rule Name'] }}"
        state: merged
        disabled: true
        audit_comment: "{{ ticket }} Disable rule"
        device_group: "{{ 'shared' if rule['Policy Name'] == 'Shared' else rule['Policy Name'] }}"
        rulebase: "{{ rule['Rule Location'] | lower }}-rulebase"
      loop: "{{ rules.list }}"
      loop_control:
        loop_var: rule

The rules variable is set using community.general.read_csv. The CSV file contains a list of security rule names, device groups etc.

Screenshots

image

Context

A CSV has been produced from another auditing tool with a list of (several hundred) rules to be disabled. I have another playbook that reads a CSV and then deletes rules which works as expected. Full disclosure: I am new to Ansible and the problem could well be me.

Your Environment

  • Panorama: 10.1.9-h1
  • Collection: 2.16.0
  • Python: 3.10.6
  • Ansible: 2.10.8
  • PAN-OS Python Library & version: pan-os-python 1.11.0, pan-python 0.17.0

🎉 Thanks for opening your first issue here! Welcome to the community!

Firstly, I get different behaviors when using state merged vs present. Things look "better" when state is set to merged.

I then changed my playbook to use include_tasks so I can run multiple plays within my loop, e.g:

    - name: Iterate rules
      include_tasks: tasks/disable_rule.yml
      loop: "{{ rules.list }}"
      loop_control:
        loop_var: rule

And then in disable_rule.yml, I gather the facts about the rule as the first play and then try to disable the rule in the second play:

- name: Get facts about rule {{ rule['Rule Name'] }}
  paloaltonetworks.panos.panos_security_rule:
    provider: "{{ provider }}"
    state: gathered
    gathered_filter: "rule_name == '{{ rule['Rule Name'] }}'"
    device_group: "{{ 'shared' if rule['Policy Name'] == 'Shared' else rule['Policy Name'] }}"
    rulebase: "{{ rule['Rule Location'] | lower }}-rulebase"
  register: original_rule

- name: Disable rule {{ rule['Rule Name'] }}
  paloaltonetworks.panos.panos_security_rule:
    provider: "{{ provider }}"
    rule_name: "{{ rule['Rule Name'] }}"
    state: merged
    disabled: true
    source_zone: "{{ original_rule.gathered.0.source_zone | default(omit, true) }}"
    source_ip: "{{ original_rule.gathered.0.source_ip | default(omit, true) }}"
    negate_source: "{{ original_rule.gathered.0.negate_source | default(omit, true) }}"
    source_user: "{{ original_rule.gathered.0.source_user | default(omit, true) }}"
    destination_zone: "{{ original_rule.gathered.0.destination_zone | default(omit, true) }}"
    destination_ip: "{{ original_rule.gathered.0.destination_ip | default(omit, true) }}"
    negate_destination: "{{ original_rule.gathered.0.negate_destination | default(omit, true) }}"
    application: "{{ original_rule.gathered.0.application | default(omit, true) }}"
    disable_server_response_inspection: "{{ original_rule.gathered.0.disable_server_response_inspection | default(omit, true) }}"
    log_start: "{{ original_rule.gathered.0.log_start | default(omit, true) }}"
    log_end: "{{ original_rule.gathered.0.log_end | default(omit, true) }}"
    log_setting: "{{ original_rule.gathered.0.log_setting | default(omit, true) }}"
    rule_type: "{{ original_rule.gathered.0.rule_type | default(omit, true) }}"
    negate_target: "{{ original_rule.gathered.0.negate_target | default(omit, true) }}"
    audit_comment: "{{ ticket }} Disable rule\n"
    device_group: "{{ 'shared' if rule['Policy Name'] == 'Shared' else rule['Policy Name'] }}"
    rulebase: "{{ rule['Rule Location'] | lower }}-rulebase"

But even then, I'm getting unexpected results from this when I look at the change preview and having to send things like source_ip, destination_ip etc seems unreasonable when the only thing that should be changed is "disabled = yes".

So I'm now using panos_type_cmd which from my initial testing "just works":

    - name: Disable rule {{ rule['Rule Name'] }}
      paloaltonetworks.panos.panos_type_cmd:
        provider: "{{ provider }}"
        xpath: |
          /config/devices/entry[@name='localhost.localdomain']
          /device-group/entry[@name='{{ rule['Policy Name'] }}']
          /{{ rule['Rule Location'] | lower }}-rulebase
          /security/rules/entry[@name='{{ rule['Rule Name'] }}']
        element:
          <disabled>yes</disabled>

I just need to write a play that uses panos_op to add an audit comment and this quest should be completed.

I'm still unsure if this is a bug or not. If this is expected behavior and I'm just "holding it wrong", please go ahead and close this issue.