Palo Alto Automation: Requesting API via PowerShell

Beginning work with a new client, one of my first tasks was automating the build for Palo Alto Virtual Appliances. The client requested that the process would be available from the Azure DevOps pipeline, and in a standard language they could use.

With these constraints, I pursued a solution using PowerShell, since it both works within the DevOps pipeline and is platform agnostic. Palo Alto also has a built-in API capable of handling most tasks that you throw its way. The next task involved is getting the two of them coalesced.

Calls with PowerShell are relatively simple, since you can request it via the commands Invoke-WebRequest or Invoke-RestMethod. As most of our calls do not require additional headers or data, Invoke-WebRequest handles them well, and is the function we utilize for most of our requests.

Requesting Palo Alto API

As mentioned above, calling a specific URL with Invoke-WebRequest is simple enough. However, when repeating the process frequently, the content of each request can make the structure difficult to parse. The ideal solution will need to make requests simple to read, and capable of handling multiple types of requests, eventually coming to fruition as the function Request-PaloApi.

Request-PaloApi takes in variables, translates them to an API request, confirms the results, and sends you back the information. A standard request for a Palo Alto includes two basic items, Key and Type. The Key is an authorization token that will generate upon request, using the keygen type, along with your username and password. This key allows you to make requests to the Palo Alto, without requiring that you pass your credentials with it. The Type specifies the kind of request you wish to perform. A type request falls into multiple categories, from the Keygen type above, to those we will discuss below.

Types for Palo Alto requests typically fall into two standard categories, Configuration and Operation. With these two, the construction will be slightly different for each one. Configuration requests ask for the location of the configuration you want to change, and what value to change. Caveats are in full effect, as an exception exists for the Delete action, where it will remove the configuration item from its destination. Operation requires the command you wish to run. This type of command is useful for getting information back from the system, whether through Show System Info, or running a Commit.

API Configuration Requests

Here’s a quick example of a Configuration Type Request.

https://$paloaddress/api/?type=config&action=set&xpath=/config/devices&element=<entry name=`"localhost.localdomain`">&key=key

This request is pointed at $paloaddress, an IP for an available Palo Alto. The request will modify the Config. By performing a Set, a non-destructive change, to the configuration. This change will occur at the XPath location, and will have the Element added to it. To confirm these changes are allowed, we will authenticate using the Key.

This format follows for most configuration actions. However, once the XPath and Element fields become substantial in size, the request is harder to recognize. For example, the request below configures three IPs on an interface, but becomes more difficult to parse. The difficulty compounds even further when including the full key.

https://$paloAddress/api/?type=config&action=set&xpath=/config/devices/entry[@name='localhost.localdomain']/network/interface/ethernet/entry
[@name='ethernet1/2']/layer3&element=<ip><entry name=`"10.1.1.1/32`"/><entry name=`"10.1.1.2/32`"/><entry name=`"10.1.1.3/24`"/></ip>&key=Zx4z6C9EbGeKgNjRnTqVtYv2y5A7CaFcHeMhPkRpUrWuZw3y6B8DaGdJfMjQmSqVsXu2x4z6C9EbGeKg
Zx4z6C9EbGeKgNjRnTqVtYv2y5A7CaFcHeMhPkRpUrWuZw3y6B8DaGdJfMjQmSqVsXu2x4z6C9EbGeKg901==

While not completely unreadable, the request becomes easily lost in translation. Making a request like this more readable is a driving force behind our function.

API Operation Requests

Moving from Configuration to Operation, the format structure differs only slightly from what we see with Configuration requests.

https://$paloaddress/api/?type=op&cmd=<show><jobs><all></all></jobs></show>&key=key

The Operation type syntax is fairly straightforward. This request runs the OPerational command specified in CMD. Once more authenticated using the KEY value. Operational commands like these, typically will not become as complex as configuration requests, however, additional readability is appreciated.

Request-PaloApi Function

Knowing what requests would make the bulk of our task, the generation of a PowerShell function with our requested capability came to fruition, and can be seen below. The code below achieves the parameters we set forth, while allowing us flexibility to expand upon the function, or supporting code to fully realize our solution.

function Request-PaloApi{  
  [CmdletBinding()]
  param(
    [string] $ipAddress,
    [string] $paloKey,
    [string] $apiType,
    [string] $apiAction,
    [string] $apiXpath,
    [string] $apiElement,
    [string] $apiCmd
  )
  
  $type = 'type=' + $apiType
  if ($apiType -eq 'config'){
	if ($apiAction -eq 'delete'){
	  $action = 'action=' + $apiAction
	  $xpath = 'xpath=' + $apiXpath
	  $paConfigUrl = ('https://{0}/api/?key={1}&{2}&{3}&{4}' -f $ipAddress, $paloKey, $type, $action, $xpath)
    }
	else{
	  $action = 'action=' + $apiAction
	  $xpath = 'xpath=' + $apiXpath
	  $element = 'element=' + $apiElement
	  $paConfigUrl = ('https://{0}/api/?key={1}&{2}&{3}&{4}&{5}' -f $ipAddress, $paloKey, $type, $action, $xpath, $element)
    }
  }
  else{
    if ($apiAction -eq 'all'){
        $cmd = 'cmd=' + $apiCmd
        $action = 'action=' + $apiAction
        $paConfigUrl = ('https://{0}/api/?key={1}&{2}&{3}&{4}' -f $ipAddress, $paloKey, $type, $action, $cmd)
    }
    else{
        $cmd = 'cmd=' + $apiCmd
        $paConfigUrl = ('https://{0}/api/?key={1}&{2}&{3}' -f $ipAddress, $paloKey, $type, $cmd)
    }
  }
  try {
	Write-Verbose -Message 'Running Config Command'
	Write-Verbose -Message ('Element Changing: {0}' -f $apiElement)
	$resultConfig = Invoke-WebRequest -Uri $paConfigUrl -ErrorVariable errorMessage
	$resultCheck = ([xml]$resultConfig.Content).response.status
	$result = ([xml]$resultConfig.Content).response.result
    Write-Verbose -Message $resultCheck
    if ($resultCheck -ne 'success'){
      throw [Net.NetworkInformation.NetworkInformationException]::New('an error occurs while retrieving network information.')
    }
  }
  catch { 
	Write-Verbose -Message 'Error received on request'
	Write-Verbose -Message ('Message From Device: {0}.response.msg.line ' -f $resultConfig)
	Write-Verbose -Message ('Message From Shell: {0}' -f $errorMessage)
	$result = 'Error on Request - Enable Verbose'
  }
  return $result
}

We are able to specify that if the type is Configuration, we set values for Type, Action, XPath, and Element. Similarly, if the type is Operational or Commit, the values Type and Cmd are set. There are exceptions to both, as the Delete Configuration Action does not require an Element, and the Commit-All Operation requires an Action to be set. With the function set, we are able to now run a command against the Palo Alto without needing to manually build our URL.

$xpath = "/config/devices/entry[@name='localhost.localdomain']/network/virtual-router/entry[@name='vr1']"
$element = '<interface><member>ethernet1/1</member><member>ethernet1/2</member></interface>'
Request-PaloApi -ipAddress $paloMgmt1 `
  -paloKey $paloKey `
  -apiType 'config' `
  -apiAction 'set' `
  -apiXpath $xpath `
  -apiElement $element

$cmd = '<show><system><info></info></system></show>'
Request-PaloApi -ipAddress $ipAddress `
  -paloKey $paloKey `
  -apiType 'op' `
  -apiCmd $cmd

With the commands above, I can identify what setting I’m changing, and it’s new value, or quickly determine a command without requiring extra parsing to pull relevant information. We also included some initial error-handling, that informs the user of when an error is received. More details can be viewed via enabling the Verbose parameter, and viewing more output from the command. Through the command, I can build additional error handling within supporting code to retry calls, or correct items that may be misconfigured.

While improvements are possible, and almost certainly on the way, the function above helps translate our requests, and make life easier when interacting with the Palo Alto API.

Palo Alto Automation: Requesting API via PowerShell

One thought on “Palo Alto Automation: Requesting API via PowerShell

  • December 24, 2019 at 1:59 am
    Permalink

    Excellently Written, very useful for our reference

    Reply

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.