Edits history of script submission #24028 for ' Push to device group (palo_alto_panos)'

  • bun
    One script reply has been approved by the moderators
    Ap­pro­ved
    import { XMLParser } from "fast-xml-parser"
    
    /**
     * Push to device group
     * 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.
     */
    export async function main(
      auth: RT.PaloAltoPanos,
      device_group: string,
      description: string | undefined,
      devices: string[] | undefined,
      exclude_template: boolean | undefined
    ) {
      let entry = `<entry name="${xmlEscape(device_group)}"`
      if (devices?.length) {
        entry += `><devices>${devices.map((d) => `<entry name="${xmlEscape(d)}"/>`).join("")}</devices></entry>`
      } else {
        entry += "/>"
      }
      let cmd = `<commit-all><shared-policy><device-group>${entry}</device-group>`
      if (description) cmd += `<description>${xmlEscape(description)}</description>`
      if (exclude_template) cmd += "<include-template>no</include-template>"
      cmd += "</shared-policy></commit-all>"
    
      const res = await xmlApi(auth, { type: "commit", action: "all", cmd })
      return {
        job_id: res.result?.job != null ? String(res.result.job) : null,
        message:
          res.result?.msg?.line ??
          res.result?.msg ??
          res.msg?.line ??
          res.msg ??
          null,
      }
    }
    
    function xmlEscape(s: string) {
      return s
        .replace(/&/g, "&amp;")
        .replace(/</g, "&lt;")
        .replace(/>/g, "&gt;")
        .replace(/"/g, "&quot;")
        .replace(/'/g, "&apos;")
    }
    
    async function xmlApi(auth: RT.PaloAltoPanos, params: { [key: string]: string }) {
      const url = new URL("/api/", auth.base_url)
      for (const [key, value] of Object.entries(params)) {
        url.searchParams.set(key, value)
      }
      const response = await fetch(url, {
        method: "POST",
        headers: { "X-PAN-KEY": auth.api_key },
      })
      const text = await response.text()
      if (!response.ok) {
        throw new Error(`${response.status} ${text}`)
      }
      const parsed = new XMLParser({
        ignoreAttributes: false,
        attributeNamePrefix: "@",
        parseTagValue: false,
      }).parse(text)
      if (parsed.response?.["@status"] !== "success") {
        throw new Error(`${response.status} ${text}`)
      }
      return parsed.response
    }
    

    Submitted by hugo989 7 hours ago