# Ansible for Firewall Automation: — Palo Alto and Fortinet Rules as Code

Source: https://ai.techclick.in/blog_ansible_firewall_automation
Markdown: https://ai.techclick.in/blog_ansible_firewall_automation.md
Publisher: Techclick Infosec Pvt Ltd

Automate Palo Alto and Fortinet firewall rules with Ansible for L1/L2 engineers and RHCE/PCNSE prep: panos vs fortios collections, the PAN-OS commit gotcha, FortiOS object-before-policy order, and safe git-gated rollout.

Ansible for Firewall Automation: — Palo Alto and Fortinet Rules as Code student learning map
                     A visual study map for Ansible for Firewall Automation: — Palo Alto and Fortinet Rules as Code showing learning path, evidence, traps, and practice sequence.

                     TECHCLICK STUDY MAP
                     Ansible for Firewall Automation: — Palo Alto and...
                     Ansible · learn the flow, prove with evidence, avoid unsafe shortcuts

   1. Start
   🎯 By the end you will be able to

   2. Understand
   Pick where you want to start

   3. Prove
   ① Firewall-as-code — killing...

   4. Practice
   ② Palo Alto with panos — objects,...

                     How to use this page
                     First build the mental model, then connect the concept to a realistic production decision. Finish by testing yourself.
                     Techclick Infosec Pvt Ltd | ai.techclick.in | Training Contact: WhatsApp +91 92772 29456

             Content-specific feature visual for this lesson: use it as the 60-second map before reading the full detail.

             Most engineers think…

             Most engineers think automating a firewall just means 'SSH into the box with Ansible and paste the CLI commands' — so they reach for the  network_cli  connection like they would for a Cisco switch.

             Wrong — and it will fail on day one. The Palo Alto and Fortinet firewall collections do  not  drive the CLI.  paloaltonetworks.panos  talks to PAN-OS over its  XML API  (via a  provider  / httpapi connection), and  fortinet.fortios  talks to FortiGate over its  REST API  (an  httpapi  connection with an  access_token ). You manage  objects and policy  declaratively, and on Palo Alto nothing is live until you  commit  — a step that has no equivalent on a switch and trips up almost everyone the first time.

## ① Firewall-as-code — killing drift, GUI clicks and the mystery rules

 Meet Sneha, an L2 engineer at  Flipkart . A change ticket lands:  "Allow the new payment service (203.0.113.40) to reach the app on tcp/8443."  She opens the Palo Alto GUI, clicks  Policies → Security , fills a form, clicks Commit. Same week, a teammate does the same change on the FortiGate by hand. Six months later an auditor asks  "why does rule 47 exist and who approved it?"  — and nobody knows. That gap is the disease this lesson cures.

 Three things go wrong with click-by-hand firewalls. First,  configuration drift : the running rulebase no longer matches any document. Second,  no review : a fat-fingered any-any rule goes live with nobody checking. Third,  no rollback : undoing a bad change means remembering what it used to be. Multiply that by two vendors with different GUIs and you have the classic outage waiting to happen.

  Rules as code  flips it. The firewall's objects and policies are described in an  Ansible playbook  kept in  git . A change is now a  pull request  a colleague reviews; a wrong change is a  git revert ; and 'why does this rule exist?' is answered by the commit message. The firewall stops being a black box you poke and becomes a file you reason about.

 👉 So far: hand-clicking two firewalls causes drift, no review, no rollback. Next: how Ansible actually reaches each box — and why it is NOT over SSH.

 Here's the part that surprises people coming from switch automation. Ansible does  not  SSH into these firewalls and type CLI.  paloaltonetworks.panos  drives PAN-OS over its  XML API  using a  provider  dict (or an httpapi connection).  fortinet.fortios  drives FortiGate over its  REST API  using the  httpapi  connection. So the connection is  httpapi  or  local  — never  network_cli . The modules speak the firewall's native API, with structured idempotency baked in.

  Figure 1 — Hand-clicked firewalls drift; rules-as-code makes every change reviewed and reversible

 A two-column comparison. Left, the manual world in red: an engineer clicks the Palo Alto GUI and another clicks the FortiGate GUI, with no review, no diff and no rollback, producing configuration drift and an undocumented rule 47. Right, the rules-as-code world in green: a single git repository holds one playbook and one inventory, a pull request is reviewed by a second engineer, the play is dry-run with check mode, and only then does Ansible drive the PAN-OS XML API and the FortiOS REST API to apply the same intent to both firewalls.

 Click-by-hand vs rules-as-code — same change, two worlds
   Manual GUI clicks (drift)
  Palo Alto GUI
  FortiGate GUI
 ✗ two people, two consoles, no review
 ✗ no diff, no approval, no audit trail
 ✗ rollback = 'remember what it was'
 ✗ config drifts from any document
  6 months later: "why does rule 47 exist?"  nobody knows · who approved it? · undocumented
   Rules as code (git + Ansible)
  git repo · 1 playbook · 1 inventory
 ✓ change = pull request, peer reviewed
 ✓ --check dry-run previews the impact
 ✓ full history → audit + git revert rollback
  panos → PAN-OS  XML API
  fortios → FortiGate  REST API
  one intent → both vendors, reviewed & reversible
   untrusted/manual drift   automation path   policy/decision   key insight   reviewed/allowed

 Left (red) = two GUIs, two people, no record → drift and mystery rule 47. Right (green) = one git repo, a PR review, --check preview, then Ansible drives PAN-OS XML API and FortiOS REST API.

### The four wins of rules-as-code, one tap each

 Tap each card — these are the lines you'll repeat to a manager who asks 'why bother automating the firewall?'

     📜
 Auditable history
 tap to flip

  Every change is a git commit with author, time and diff. So: 'who changed rule 47 and why' is answered, not guessed.

    👀
 Peer review
 tap to flip

  A change is a PR a second engineer approves before merge. So: a fat-fingered any-any rule gets caught off the firewall.

    ↩️
 Easy rollback
 tap to flip

  Undo is git revert + re-run, not 'remember the old value'. So: a bad rule is gone in minutes, cleanly.

    🔁
 Two vendors, one intent
 tap to flip

  One inventory drives Palo Alto and Fortinet from the same source of truth. So: no more divergent hand-config per box.

  Daily-life analogy — the society gate-pass register
 Manual firewall clicks are like a security guard who lets visitors in on a nod and never writes it down — a month later nobody knows who entered or why. Rules-as-code is the  society gate-pass register : every entry is logged with who, when and approved-by, the watchman follows a written list, and if a wrong name got in you can see exactly which entry to strike off. The firewall rulebase is your building; git is the register.

  Pause &amp; Predict

 Predict: you point Ansible at a Palo Alto firewall using the network_cli connection (the one you'd use for a Cisco switch). What goes wrong? Type your guess.

   Reveal answer   Answer:  It fails. The paloaltonetworks.panos modules don't drive the CLI — they call the PAN-OS XML API through a provider dict or an httpapi connection. With network_cli there's no API session for the module to use, so tasks error out on connection/auth. The fix is to use the provider params (or set connection: httpapi for the API plugin) — never network_cli for these firewall collections.

  Quick check · Q1 of 10
 Aditya at Wipro asks: "What's the single biggest reason to move firewall changes from the GUI into Ansible playbooks in git?"

    a) It makes the firewall faster at forwarding packets     b) Every change becomes reviewable, auditable and reversible code instead of an undocumented click     c) It removes the need for any firewall rules at all     d) It lets you SSH into the firewall more quickly
  Correct: b.  The point of rules-as-code is governance: a change is a reviewed PR with a full git history you can audit and revert — killing drift and 'mystery rule 47'. It has nothing to do with forwarding speed, you still need rules (you're just defining them differently), and these collections deliberately don't SSH to the CLI at all.

## ② Palo Alto with panos — objects, security rules, and the commit step

 Let's automate the Flipkart change properly on Palo Alto. Two ingredients:  objects  (named things like an address) and a  security rule  that references them. With  panos_address_object  you create the address  partner-pay-host  = 203.0.113.40, and with  panos_security_rule  you write the allow rule. The object comes first so the rule can point at it by name.

 Connection is via the  provider  dict —  ip_address ,  username , and a  password  or  api_key  (the modern path is an httpapi connection, but provider is what you'll see most). Key rule fields map straight to the GUI you already know:  source_zone  /  destination_zone  (e.g.  untrust  →  dmz ),  source_ip  /  destination_ip ,  application  (App-ID, e.g.  web-browsing  or  ssl ),  service  (defaults to  application-default ),  action  ( allow / deny / drop ),  tag_name , and crucially  location  ( top / bottom / before / after ) which controls where in the rulebase it lands.

 👉 So far: object first, then a security rule, fields mirror the GUI. Next: the step everyone forgets — the commit.

 Now the gotcha that catches every newcomer. Your  panos_security_rule  task writes to the  candidate config , not the  running config . Nothing changes for users until a  commit  copies candidate to running. So a panos playbook is incomplete without  panos_commit_firewall  at the end. The inline  commit: true  parameter on the rule module is  deprecated  — use the dedicated commit module instead.

 If you're driving  Panorama  instead of a single firewall, two more things change. You target a  device_group  (and optionally a template for network config), and committing is two steps:  panos_commit_panorama  commits Panorama, then  panos_commit_push  pushes it down to the firewalls. Miss the push and your rule lives on Panorama but never reaches the box enforcing traffic.

  Figure 2 — On Palo Alto, a rule isn't live until commit copies candidate to running

 A left-to-right flow. The Ansible control node runs three tasks: panos_address_object then panos_security_rule write into the PAN-OS candidate configuration, shown amber to mean not yet enforcing. A third task, panos_commit_firewall, commits candidate to the running configuration, shown green to mean now enforcing traffic. A separate branch shows the Panorama path where panos_commit_panorama then panos_commit_push are needed to reach managed firewalls. A break note warns that forgetting the commit leaves the rule in candidate and traffic unchanged.

 PAN-OS: candidate → (commit) → running
  Ansible control  paloaltonetworks.panos  XML API · provider
  Candidate config (not enforcing)
  1· panos_address_object  partner-pay-host = 203.0.113.40
  2· panos_security_rule  untrust→dmz · ssl · allow · location: bottom

- Running config now enforcing traffic 3· panos_commit _firewall candidate → running commit Panorama path: device_group → commit then PUSH panos_commit_panorama → panos_commit_push → reaches managed firewalls Key insight no commit = rule invisible to traffic Forget the commit → play says 'changed', users still blocked. Inline commit: true is deprecated; use the commit module. forgot / blocked automation path candidate/decision key insight running/enforcing Follow the arrows: panos tasks write the OBJECT then the RULE into candidate (amber). Users are unaffected until panos_commit_firewall copies candidate → running (green). On Panorama add a push to the firewalls. ansible-playbook — Palo Alto: object first, security rule, then COMMIT (the step everyone forgets) - name: Flipkart payment allow on PAN-OS hosts: paloalto connection: local gather_facts: false vars: provider: ip_address: "&#123;&#123; pan_ip &#125;&#125;" username: "&#123;&#123; pan_user &#125;&#125;" api_key: "&#123;&#123; pan_api_key &#125;&#125;" # from vault — next lesson tasks: - name: 1) Address object FIRST paloaltonetworks.panos.panos_address_object: provider: "&#123;&#123; provider &#125;&#125;" name: "partner-pay-host" value: "203.0.113.40" description: "Partner payment service" - name: 2) Security rule that references it paloaltonetworks.panos.panos_security_rule: provider: "&#123;&#123; provider &#125;&#125;" rule_name: "allow-partner-pay" source_zone: ["untrust"] destination_zone: ["dmz"] source_ip: ["partner-pay-host"] destination_ip: ["app-pay-server"] application: ["ssl"] action: "allow" tag_name: ["change-CHG10422"] location: "bottom" - name: 3) COMMIT — candidate -> running (or it never enforces) paloaltonetworks.panos.panos_commit_firewall: provider: "&#123;&#123; provider &#125;&#125;" Expected output PLAY [Flipkart payment allow on PAN-OS] **************************** TASK [1) Address object FIRST] ************************ changed: [pa-fw-01] TASK [2) Security rule that references it] *********** changed: [pa-fw-01] TASK [3) COMMIT - candidate -> running] ************** changed: [pa-fw-01] PLAY RECAP ********** pa-fw-01 : ok=3 changed=3 unreachable=0 failed=0 Palo Alto Web UI → Policies → Security → (your rule) , with the pending-changes/Commit button lit. (Recreated for clarity — your console matches this.)"> 🖥️ The rule your playbook wrote, seen in the GUI — Palo Alto Web UI → Policies → Security → (your rule) , with the pending-changes/Commit button lit. (Recreated for clarity — your console matches this.) PA-VM Web UI · Policies → Security 1 Name allow-partner-pay 2 Source Zone untrust 3 Destination Zone dmz Application ssl Action Allow 4 Status uncommitted change pending Commit Common mistake — 'the play said changed but my rule isn't working' Symptom: ansible-playbook reports changed for your panos_security_rule task, but traffic is still blocked and the GUI shows an uncommitted change . Cause: you wrote candidate config but never committed it to running. Fix: add a panos_commit_firewall task (single firewall) or panos_commit_panorama + panos_commit_push (Panorama). Real-world twist from the field: a known bug after upgrading Panorama to 11.x made panos_commit_panorama report success while the config stayed on candidate (it worked on 10.2.8) — so always verify the running config, not just the task result. Quick check · Q2 of 10 Neha at HCL automates a Palo Alto allow rule. The play finishes with ok=2 changed=2 and no errors, but users still can't reach the app. The GUI shows 'changes pending'. What's missing from her playbook? a) A reboot task after the rule b) She used the wrong collection — should be fortinet.fortios c) A panos_commit_firewall task to copy candidate config to running d) A network_cli connection block Correct: c. 'Changes pending' is PAN-OS telling you the rule is in candidate, not running. Adding panos_commit_firewall commits it so it enforces. PAN-OS doesn't need a reboot, the collection is correct for a Palo Alto box, and these modules don't use network_cli. Pause & Predict Predict: on Panorama you run panos_security_rule into a device_group and panos_commit_panorama, but skip panos_commit_push. Will the managed firewalls enforce the rule? Type your guess. Reveal answer Answer: No. panos_commit_panorama only commits Panorama's own candidate config; the rule now exists in Panorama's committed config but hasn't been pushed to the firewalls that actually enforce traffic. You also need panos_commit_push to push the device-group config down to the managed firewalls. Commit-then-push is two distinct steps on Panorama — miss the push and the box keeps enforcing its old rulebase. ## ③ Fortinet with fortios — REST, VDOMs, tokens and object order Same change, now on the FortiGate. The fortinet.fortios collection talks to the box over its REST API using the httpapi connection plugin (set ansible_connection: httpapi ). The old fortiosapi Python library path is deprecated — httpapi is the supported way now. So, like Palo Alto, you're driving an API, not SSH-ing to the CLI. Auth is a REST API token . You generate it on the box under System → Administrators → Create New → REST API Admin , then pass it to tasks as access_token (kept in Vault, of course). Every fortios task also names a vdom — default root — so your objects land in the right virtual firewall. Put an object in the wrong vdom and the policy in another vdom can't see it. 👉 So far: REST over httpapi, an access_token, and a vdom per task. Next: the ordering trap — objects before the policy that uses them. The FortiGate version of the object-first rule is strict and bites people constantly. A fortios_firewall_policy references its source/destination by the names of fortios_firewall_address objects. If the policy task runs before those address objects exist, FortiOS rejects it with entry not found in datasource . Ansible runs tasks top-down, so the fix is just ordering: address tasks above the policy task. One more FortiGate-specific wrinkle: policy position and idempotency . A policy has a numeric policyid and an order in the list (FortiGate evaluates top-down, first match wins). Creating/updating a policy by id is idempotent — run it twice, the second run is ok not changed . But moving a policy's position is a separate action: move (before/after) operation; if you rely on creation order alone, re-running may not re-assert position, so be explicit when order matters to avoid rule shadowing . Figure 3 — FortiGate: the address object must exist before the policy that names it — or the task errors Two rows comparing task order on FortiGate over the fortios httpapi REST connection. The top row, correct and green: task one creates a fortios_firewall_address object partner-pay-host, task two creates pay-app, task three creates the fortios_firewall_policy that references both by name and succeeds. The bottom row, wrong and red: the policy task runs first, references names that do not exist yet, and FortiOS returns entry not found in datasource. Every task carries a vdom set to root and an access_token for authentication. FortiGate task order: object-before-policy (REST · httpapi · vdom: root) ✓ Correct order — objects first 1· fortios_firewall_address partner-pay-host 203.0.113.40 2· fortios_firewall_address pay-app 172.16.40.9/32 3· fortios_firewall_policy srcaddr/dstaddr resolve → accept policy live no commit needed ✗ Wrong order — policy first 1· fortios_firewall_policy srcaddr: partner-pay-host (missing!) FortiOS error entry not found in datasource Position matters too: top-down, first match wins → a broad rule above a tight one = shadowing wrong order / error task flow position/decision key insight correct / live Top row (green, correct): address objects created first, then the policy resolves their names. Bottom row (red, wrong): policy first → FortiOS returns 'entry not found in datasource'. Note the vdom and access_token on every task. ### ▶ Watch one FortiGate change land over REST Sneha's same payment-allow change, now on the FortiGate. Follow it from the Ansible control node through the httpapi REST session into vdom root. Press Play for the healthy path, then Break it to see the failure. ① Connect control node opens httpapi session to fortigate , auth via access_token , vdom=root ▼ ② Address obj fortios_firewall_address creates partner-pay-host = 203.0.113.40 in vdom root ▼ ③ Policy fortios_firewall_policy id 12: srcaddr resolves the named object → action accept ▼ ④ Live REST write applied immediately — no commit step ; re-run reports ok (idempotent) Press Play to step through the healthy path. Then press Break it . ▶ Play Next ▶ ⚠ Break it ↺ Reset ansible-playbook — FortiGate: httpapi + access_token, addresses first, then the policy (vdom root) - name: Flipkart payment allow on FortiGate hosts: fortigate connection: httpapi gather_facts: false vars: vdom: "root" ansible_httpapi_use_ssl: true ansible_httpapi_validate_certs: false tasks: - name: 1) Address objects FIRST fortinet.fortios.fortios_firewall_address: access_token: "&#123;&#123; forti_token &#125;&#125;" # from vault vdom: "&#123;&#123; vdom &#125;&#125;" state: present firewall_address: name: "partner-pay-host" type: ipmask subnet: "203.0.113.40 255.255.255.255" - name: 2) Policy that references the object by name fortinet.fortios.fortios_firewall_policy: access_token: "&#123;&#123; forti_token &#125;&#125;" vdom: "&#123;&#123; vdom &#125;&#125;" state: present firewall_policy: policyid: 12 name: "allow-partner-pay" srcintf: [{ name: "port2" }] dstintf: [{ name: "port1" }] srcaddr: [{ name: "partner-pay-host" }] dstaddr: [{ name: "pay-app" }] service: [{ name: "HTTPS" }] schedule: "always" action: "accept" status: "enable" Expected output PLAY [Flipkart payment allow on FortiGate] ************************ TASK [1) Address objects FIRST] ********************** changed: [fg-01] TASK [2) Policy that references the object by name] ** changed: [fg-01] PLAY RECAP ********** fg-01 : ok=2 changed=2 unreachable=0 failed=0 # re-run -> ok=2 changed=0 (idempotent: state already matches) Prove the FortiGate policy is real, not just 'changed' After the play, confirm the object and policy exist in the right vdom rather than trusting the task result. Quick check on the box: config vdom → edit root → show firewall address partner-pay-host and show firewall policy 12 should both return your config. Better still, re-run the playbook with --check : if everything reports ok (no changed ), reality already matches your code — that's idempotency proving the change stuck. Arjun at ICICI faces this Arjun, an L1 engineer, runs the FortiGate firewall playbook and the policy task fails immediately with 'entry not found in datasource' on srcaddr. The address task and the policy task are both in the file. Likely cause Task order. The policy task is listed ABOVE the fortios_firewall_address tasks, so Ansible runs the policy first — and it references object names that FortiOS hasn't created yet. (Or the objects were created in a different vdom than the policy.) Diagnosis He reads the play top-to-bottom like Ansible does, confirms the policy is above the address objects, and checks that both tasks use the same vdom (root). playbook tasks: list fortios_firewall_address ABOVE fortios_firewall_policy; verify vdom matches on both (System → VDOM on the GUI) Fix Reorder so the two fortios_firewall_address tasks come first, then the fortios_firewall_policy, all in vdom root. Re-run the play. Verify Play now reports changed for the address objects and the policy; show firewall policy 12 on the box lists srcaddr partner-pay-host, and traffic from 203.0.113.40 reaches the app. Quick check · Q3 of 10 Meera at Airtel writes a FortiGate policy referencing srcaddr 'branch-net' and runs it. It fails with 'entry not found in datasource'. The play has both an address task and the policy task. Most likely cause? a) The access_token is for the wrong cloud region b) FortiGate requires a commit step the play is missing c) She should use paloaltonetworks.panos for FortiGate d) The fortios_firewall_policy task runs before the fortios_firewall_address that defines 'branch-net' (or it's in a different vdom) Correct: d. A FortiGate policy resolves srcaddr/dstaddr by object name, so the address object must be created first and in the same vdom — wrong order (or wrong vdom) gives 'entry not found in datasource'. FortiGate config has no commit step, the panos collection is for Palo Alto, and a token region mismatch would give an auth/connection error, not a datasource error. Pause & Predict Predict: you run the FortiGate playbook twice in a row without changing anything. What does the PLAY RECAP show on the second run, and what does that tell you? Type your guess. Reveal answer Answer: The second run shows changed=0 (everything ok). That's idempotency: the modules describe a desired state, the first run made it real, and the second run finds reality already matches — so nothing is rewritten. It's the proof that your rules-as-code is safe to re-run on a schedule without churning the firewall, and a quick way to confirm a change actually stuck. ## ④ Safe rollout — git, --check, gated commit, both vendors from one run You can write rules in code now. The discipline that makes it safe is the rollout flow : playbooks live in git, every change is a pull request a colleague reviews, the play is dry-run with --check , and the commit/push is a deliberate gated step — never an accident. Manual GUI clicking had none of these gates; this is where the payoff is. Check mode is your preview. ansible-playbook site.yml --check --diff reports which rules would be created or changed, and the diff shows before/after — all without touching the firewall. On Palo Alto you get a second safety net for free: even a real run leaves edits in candidate config until you run the commit task, so you can stage changes and commit only after a human says go. That commit becomes your gate. 👉 So far: git PR + --check + a gated commit. Next: one inventory pushing the same rule to BOTH vendors, then the gotchas to dodge. Now the headline trick: one inventory, both vendors . You group your boxes — a paloalto group and a fortigate group — and keep the intent once in group_vars (allow 203.0.113.40 → the payment app on HTTPS). One play targets the paloalto group with panos modules (+ commit); another targets the fortigate group with fortios modules. The same business change is translated into each vendor's object + rule syntax from a single source of truth. Terminal — the gated rollout: branch, dry-run, review, then run for real (both vendors) arjun@netauto:~/firewall-iac$ git checkout -b chg10422-allow-pay arjun@netauto:~/firewall-iac$ ansible-playbook -i inventory.ini site.yml --check --diff arjun@netauto:~/firewall-iac$ git commit -am "CHG10422 allow 203.0.113.40 -> pay-app (HTTPS)" arjun@netauto:~/firewall-iac$ git push origin chg10422-allow-pay # open PR, get review # --- after a teammate approves the PR --- arjun@netauto:~/firewall-iac$ ansible-playbook -i inventory.ini site.yml Expected output PLAY [Palo Alto] ** changed: address-object, security-rule, COMMIT (candidate->running) PLAY [FortiGate] ** changed: firewall_address x2, firewall_policy (REST, live) PLAY RECAP ********** pa-fw-01 : ok=3 changed=3 failed=0 fg-01 : ok=3 changed=3 failed=0 Figure 4 — One reviewed change flows to both vendors — and the only deliberate live moment is the commit A left-to-right pipeline. Stage one, an engineer creates a git branch and opens a pull request reviewed by a second engineer. Stage two, the play is run with check mode to preview which rules would change. Stage three, after approval the branch merges. Stage four, a single ansible-playbook run uses one inventory to drive both vendors: the Palo Alto group via paloaltonetworks.panos ending in a deliberate commit gate, and the FortiGate group via fortinet.fortios applied live over REST. Amber marks the commit decision gate, green marks the enforced result. Gated rollout: review → preview → merge → one run, both vendors 1· branch + PR peer reviews the diff 2· --check --diff preview, no writes 3· merge approved → main 4· one ansible-playbook run -i inventory.ini (paloalto + fortigate) Palo Alto group · panos address_object → security_rule COMMIT = the deliberate gate candidate stays safe until you say go FortiGate group · fortios firewall_address → firewall_policy REST write = live immediately no commit; idempotent re-runs Same intent in group_vars → two vendor translations → reviewed, previewed, reversible review gate pipeline path commit gate key insight live/enforcing Read left to right: a branch + PR (reviewed) → --check preview → merge → one run that drives panos (commit gate) and fortios (live REST) from the SAME inventory. Red = the gates that stop a bad change. Why bother with all this rigour? Because the perimeter is a live target. In 2024 , PAN-OS CVE-2024-3400 (GlobalProtect command injection, CVSS 10.0) was actively exploited in the wild, and CVE-2024-0012 let attackers bypass the management web interface auth. When the next advisory drops, the team that can review, dry-run and roll out a hardening rule across every firewall from code in minutes — with an audit trail — is in a very different place from the team clicking 40 GUIs by hand at 2 a.m. Rules-as-code isn't just tidiness; it's how you respond fast and prove what you did. Figure 5 — Ansible firewall automation — the cheat-sheet A nine-tile cheat sheet. Tiles cover the Palo Alto module set, the Fortinet module set, the connection model for each, the candidate-commit model on PAN-OS, the no-commit live model on FortiGate, the two ordering and commit gotchas, rule shadowing and position, the safe rollout commands, and the one-inventory both-vendors idea. Ansible firewall automation — your one-glance card Palo Alto — panos panos_address_object panos_security_rule panos_commit_firewall Panorama: + commit_panorama/push Fortinet — fortios fortios_firewall_address fortios_firewall_policy vdom (root), state: present legacy fortiosapi = deprecated Connection model panos: provider / httpapi (XML API) fortios: httpapi + access_token (REST) NEVER network_cli for these tokens/keys → Ansible Vault PAN-OS commit model edits → candidate config commit → running (now live) inline commit: true = deprecated no commit = rule not enforcing FortiGate model each REST write = live now no candidate / no commit step re-run = ok (idempotent) verify: show firewall policy N Top 3 gotchas 1· PAN: forgot the commit 2· Forti: object before policy 3· rule shadowing (position) entry not found in datasource Safe rollout flow git branch → PR review ansible-playbook --check --diff merge → run → gated commit git revert = clean rollback One inventory, both groups: paloalto / fortigate intent once in group_vars each play → its vendor modules 1 truth → 2 translations Cert angle RHCE EX294: playbooks, roles, collections, Vault PCNSE/NSE: automate the box why CVE-2024-3400 → automate fast Your one-card map: panos vs fortios module names, connection model, the commit/gate flow, the two big gotchas, and the safe-rollout commands. Keep it open in your first week and before any PCNSE/NSE or RHCE interview. Daily-life analogy — the dabbawala two-line delivery Your single change ('allow the partner host to the payment app') is one tiffin order. The group_vars is the address written once on the lid. The Mumbai dabbawala system reads that one address and routes it through whichever line is right — the panos line commits at the destination gate (the PAN-OS commit), the fortios line drops it straight in (live REST). One order, written once, delivered correctly to two different buildings by two different routes. That's one inventory driving both vendors. Prove you own firewall-as-code Cold, in 30 seconds: name the panos modules ( panos_address_object → panos_security_rule → panos_commit_firewall ) and the fortios modules ( fortios_firewall_address → fortios_firewall_policy ); say the connection model for each ( panos = provider/httpapi XML API; fortios = httpapi REST + access_token; never network_cli ); state the two big gotchas ( PAN forgot-commit; Forti object-before-policy ); and describe the safe rollout ( git PR → --check → gated commit ). If you can do that without notes, you're ready for the next lesson and for the automation questions in PCNSE/NSE and RHCE. Next: Ansible Vault — encrypt those tokens & passwords Related: Ansible Playbooks for Cisco IOS Quick check · Q4 of 10 An interviewer asks Sneha: "Give me the single most important reason to put firewall changes behind a git PR + --check + a gated commit, instead of pushing playbooks straight from your laptop." Best answer? a) It makes the playbook run faster b) It gives peer review, a dry-run preview, an audit trail and a clean rollback — the governance manual GUI clicks never had c) It lets you skip writing the address objects d) It removes the need to commit on Palo Alto Correct: b. The whole value of rules-as-code is governance: a reviewed PR catches mistakes, --check previews impact, git gives an audit trail and git revert rollback, and the gated commit makes the live moment deliberate. It's not about speed, you still need objects, and Palo Alto still requires its commit step regardless of rollout flow. ### 🤖 Ask the AI Tutor Tap any question — instant, scoped to this lesson. No login, no waiting. Why can't I just use network_cli and paste CLI commands like I do for Cisco? What exactly is the 'commit' step on Palo Alto, and why does Fortinet not need it? On FortiGate, why must I create the address object before the policy? How do I store the firewall password / API token safely in the playbook? How do I preview a firewall change before it actually happens? How does one playbook push the same rule to BOTH Palo Alto and Fortinet? Pre-curated from Ansible docs + community Q&A, scoped to this lesson. For a live prod issue, paste your export into chat.techclick.in. ## 📝 Wrap-up assessment — six more You've answered 4 inline. Six left. 70% (7 of 10) marks the lesson complete on your profile. Tap Submit all answers at the end. Q5 · Remember Which Ansible collection and connection model does PAN-OS use, and what activates a rule it created? a) paloaltonetworks.panos over the XML API (provider/httpapi); a commit activates the candidate config b) fortinet.fortios over REST; a commit activates it c) cisco.ios over network_cli; saving the config activates it d) paloaltonetworks.panos over network_cli; a reboot activates it Correct: a. PAN-OS is driven by paloaltonetworks.panos over its XML API (via a provider dict or httpapi), and edits sit in candidate config until a commit makes them live. fortinet.fortios is the FortiGate collection; cisco.ios/network_cli is for IOS switches; PAN-OS firewall automation does not use network_cli and does not need a reboot. Q6 · Apply Priya at Infosys must allow a partner host 203.0.113.40 to reach a payment app on a Palo Alto firewall. She writes a panos_address_object task then a panos_security_rule task and runs the play. The play succeeds but users still can't reach the app. What did she most likely forget? a) To add a panos_commit_firewall task so the candidate config becomes running b) To reboot the firewall after the play c) To use the network_cli connection d) To create the address object — modules don't support objects Correct: a. Her tasks wrote to the candidate config; without a panos_commit_firewall (or commit + push on Panorama) the rule never reaches the running config, so it isn't enforcing yet. PAN-OS doesn't need a reboot, panos doesn't use network_cli, and panos_address_object is exactly how you create objects — she did create it. Q7 · Apply On a FortiGate (vdom 'root'), Rahul at TCS wants a policy that permits source object 'partner-host' to destination 'pay-app'. The policy task fails with 'entry not found in datasource'. What is the fix? a) Set the connection to network_cli b) Create the fortios_firewall_address objects for partner-host and pay-app ABOVE the fortios_firewall_policy task c) Add a commit task at the end of the play d) Switch the vdom to 'global' Correct: b. A FortiGate policy references address objects by name, so those fortios_firewall_address objects must exist first — ordering them above the policy task fixes the 'entry not found in datasource' error. FortiGate config has no separate commit step, fortios uses httpapi not network_cli, and 'global' is not where these firewall objects live. Q8 · Analyze A team's playbook adds an 'allow any-any for testing' rule at the TOP of the Palo Alto rulebase, above the existing tight rules. The play commits cleanly and traffic flows, but a later audit flags a security hole. What happened, in rulebase terms? a) The commit failed silently so nothing changed b) FortiOS overrode the Palo Alto rule c) Rule shadowing — the broad top rule is evaluated first and matches everything, so the tighter rules below it never apply d) Idempotency removed the tight rules on the second run Correct: c. Firewalls evaluate top-down and stop at the first match, so a broad allow placed at the top (via location: 'top') shadows every stricter rule beneath it — a classic automation mistake when you don't control rule position. The commit clearly worked (traffic flowed), FortiOS isn't involved on a Palo Alto box, and idempotency doesn't delete unrelated rules. Q9 · Analyze Karthik runs the firewall playbook with ansible-playbook site.yml --check and it reports two tasks as 'changed'. He runs the real play, then runs --check again and now everything is 'ok'. What does this prove? a) The firewall is broken because nothing changed the second time b) Check mode actually wrote the config the first time c) The vdom changed between runs d) The modules are idempotent — the first run made the changes, and re-running matches the desired state so there's nothing left to change Correct: d. Idempotency means the playbook describes a desired state: the first real run created the rules, so a second --check finds reality already matches and reports 'ok' (no changes). Check mode never writes config (that's the point of --check), a steady 'ok' is healthy not broken, and the vdom didn't change. Q10 · Evaluate Two rollout designs for firewall-as-code. (A) Engineers push playbooks straight to the firewalls from their laptops, committing immediately, and keep a copy of the YAML in a shared folder. (B) Playbooks live in git; every change is a pull request reviewed by a second engineer, run with --check in CI, and the commit/push is a separate gated job after approval. Which is stronger and why? a) B — git PR review + --check + a separate gated commit give you peer review, a dry-run preview, an audit trail and a clean rollback, which is the whole point of rules-as-code b) A — it's faster and the shared folder is enough of a record c) A — committing immediately avoids candidate-config confusion d) Both are equivalent once the YAML exists Correct: a. B delivers exactly what manual GUI clicks lacked: a reviewed change (PR), a preview of impact (--check), a full history to audit and revert (git), and a deliberate moment to commit rather than an accidental one. A's laptop-push with an immediate commit and a shared folder reproduces the drift and 'who changed rule 47?' problem you were trying to kill; immediate commit removes the safety the candidate model gives you. Submit all answers Try again Lesson complete — saved to your profile. Almost! You need 70% (7 of 10) — re-read the path that tripped you up and tap "Try again". ### 🧠 In your own words Type one line: In one line, why is a Palo Alto rule created by Ansible not actually enforcing traffic until a separate step runs? Then compare to the expert version. Compare with expert answer Expert version: Because PAN-OS edits land in the candidate configuration; they only start enforcing once a commit (e.g. panos_commit_firewall ) copies candidate to the running config — so a panos playbook is incomplete without that commit task. ### 🗣 Teach a friend Best way to lock it in — explain it in one line to a teammate. Tap to generate a paste-ready summary. Generate my one-liner 📩 Quiz me on this in 7 days. Opt in and we'll email 3 micro-questions on Firewall Automation at Day 1, Day 7 and Day 30 — spaced repetition is how this sticks. Un-tick any time. ### 📖 Glossary Rules as code Defining firewall objects and policies in version-controlled files (playbooks) instead of clicking a GUI, so changes are reviewed, audited and repeatable. paloaltonetworks.panos The official Ansible collection for PAN-OS firewalls and Panorama; drives the device over its XML API, not the CLI. fortinet.fortios The official Ansible collection for FortiGate/FortiOS; drives the device over its REST API using the httpapi connection. Candidate vs running config On PAN-OS, candidate holds your edits; running is what actually enforces traffic. A commit copies candidate to running. panos_commit_firewall The panos module that commits a firewall's candidate config to running, making earlier rule/object tasks take effect. provider (panos) A dict of PAN-OS connection details (ip_address, username, password or api_key) passed to panos tasks; the modern alternative is an httpapi connection. httpapi connection An Ansible connection plugin that talks to a device's HTTP API. Both panos (optionally) and fortios use it instead of network_cli/SSH. access_token (fortios) A REST API token generated on FortiGate (System → Administrators → REST API Admin) that authenticates fortios tasks; safer than a reused admin password. VDOM Virtual Domain — a logical firewall instance inside one FortiGate. fortios tasks target a vdom (default 'root') so objects land in the right partition. device_group (Panorama) On Panorama, the container that holds shared objects and rules pushed to a set of managed firewalls; panos tasks set device_group to target it. Object-before-policy The ordering rule: an address/service object must exist before a policy can reference it by name, or the policy task errors out. --check mode Ansible dry-run: reports which tasks would change without writing anything, so you preview a firewall change before it is real. Idempotency Running the same playbook twice leaves the firewall in the same state — the second run reports 'ok', not 'changed', because the rule already matches. #### 📚 Sources paloaltonetworks.panos.panos_security_rule module — parameters (provider, device_group, rule_name, source_zone/destination_zone, source_ip/destination_ip, application, service, action, tag_name, location) and the deprecated inline 'commit'. paloaltonetworks.github.io/pan-os-ansible/modules/panos_security_rule_module.html

- paloaltonetworks.panos.panos_commit_firewall / panos_commit_panorama / panos_commit_push — committing candidate to running; Panorama commit then push to managed firewalls. paloaltonetworks.github.io/pan-os-ansible/modules/panos_commit_firewall_module.html

- fortinet.fortios.fortios_firewall_policy and fortios_firewall_address modules — vdom, access_token, srcintf/dstintf/srcaddr/dstaddr/service/action/schedule, httpapi connection; legacy fortiosapi deprecated in favour of httpapi. docs.ansible.com/.../fortinet/fortios/fortios_firewall_policy_module.html

- PaloAltoNetworks/pan-os-ansible GitHub issue #569 — real gotcha: after upgrading Panorama to 11.x, panos_commit_panorama reports success but the config stays on candidate and isn't pushed (was fine on 10.2.8). github.com/PaloAltoNetworks/pan-os-ansible/issues/569

- OneUpTime engineering blog — 'How to Use Ansible to Manage FortiGate Firewalls' (Feb 2026): generating a REST API Admin token under System → Administrators, ansible_httpapi setup, address-then-policy ordering. oneuptime.com/blog/post/2026-02-21-how-to-use-ansible-to-manage-fortigate-firewalls/view

- PAN-OS CVE-2024-3400 (GlobalProtect command injection, CVSS 10.0, actively exploited) and CVE-2024-0012 (mgmt-web auth bypass) — why fast, repeatable, auditable firewall change/patching via code matters. security.paloaltonetworks.com/CVE-2024-3400 · cisa.gov alert 2024/04/12

- Red Hat RHCE EX294 objectives (manage Ansible content, write playbooks with tasks/variables, use roles and collections, encrypt with Ansible Vault) + Palo Alto PCNSE / Fortinet NSE automation angle. redhat.com/en/services/training/ex294-red-hat-certified-engineer-rhce-exam-red-hat-enterprise-linux-8-exam

### What's next?

             You just put firewall passwords and API tokens into variables — now stop them sitting in plaintext. Next we encrypt them with Ansible Vault so your rules-as-code repo is safe to share.

                 Next · Ansible Vault: Encrypting the Passwords in Your Playbooks →
                 Practice on exam.techclick.in →

---
Cite this Techclick lesson with the source URL. Do not invent fees, batch dates, or job guarantees.
Browse all lessons: https://ai.techclick.in/blogs
AI index: https://ai.techclick.in/llms.txt
