0

Push to device group

by
Published today

Push the committed Panorama configuration to the firewalls of a device group (PAN-OS XML API, type=commit&action=all, commit-all shared-policy). Commit on Panorama first. Set devices (firewall serial numbers) to push to only those members; set exclude_template to leave out template changes. Returns the job id to follow with Get job status.

Script palo_alto_panos Verified

The script

Submitted by hugo989 Bun
Verified 7 hours ago
1
import { XMLParser } from "fast-xml-parser"
2

3
/**
4
 * Push to device group
5
 * Push the committed Panorama configuration to the firewalls of a device group (PAN-OS XML API, type=commit&action=all, commit-all shared-policy). Commit on Panorama first. Set devices (firewall serial numbers) to push to only those members; set exclude_template to leave out template changes. Returns the job id to follow with Get job status.
6
 */
7
export async function main(
8
  auth: RT.PaloAltoPanos,
9
  device_group: string,
10
  description: string | undefined,
11
  devices: string[] | undefined,
12
  exclude_template: boolean | undefined
13
) {
14
  let entry = `<entry name="${xmlEscape(device_group)}"`
15
  if (devices?.length) {
16
    entry += `><devices>${devices.map((d) => `<entry name="${xmlEscape(d)}"/>`).join("")}</devices></entry>`
17
  } else {
18
    entry += "/>"
19
  }
20
  let cmd = `<commit-all><shared-policy><device-group>${entry}</device-group>`
21
  if (description) cmd += `<description>${xmlEscape(description)}</description>`
22
  if (exclude_template) cmd += "<include-template>no</include-template>"
23
  cmd += "</shared-policy></commit-all>"
24

25
  const res = await xmlApi(auth, { type: "commit", action: "all", cmd })
26
  return {
27
    job_id: res.result?.job != null ? String(res.result.job) : null,
28
    message:
29
      res.result?.msg?.line ??
30
      res.result?.msg ??
31
      res.msg?.line ??
32
      res.msg ??
33
      null,
34
  }
35
}
36

37
function xmlEscape(s: string) {
38
  return s
39
    .replace(/&/g, "&amp;")
40
    .replace(/</g, "&lt;")
41
    .replace(/>/g, "&gt;")
42
    .replace(/"/g, "&quot;")
43
    .replace(/'/g, "&apos;")
44
}
45

46
async function xmlApi(auth: RT.PaloAltoPanos, params: { [key: string]: string }) {
47
  const url = new URL("/api/", auth.base_url)
48
  for (const [key, value] of Object.entries(params)) {
49
    url.searchParams.set(key, value)
50
  }
51
  const response = await fetch(url, {
52
    method: "POST",
53
    headers: { "X-PAN-KEY": auth.api_key },
54
  })
55
  const text = await response.text()
56
  if (!response.ok) {
57
    throw new Error(`${response.status} ${text}`)
58
  }
59
  const parsed = new XMLParser({
60
    ignoreAttributes: false,
61
    attributeNamePrefix: "@",
62
    parseTagValue: false,
63
  }).parse(text)
64
  if (parsed.response?.["@status"] !== "success") {
65
    throw new Error(`${response.status} ${text}`)
66
  }
67
  return parsed.response
68
}
69