Publishing information to the Jamf Pro server using API calls and Python

Publishing information to the Jamf Pro server using API calls and Python

Like a loyal droid, your Jamf Pro server wants to help! (Part two)

Jamf Software’s Jamf Pro server (JPS) provides an Application Programming Interface, or API, to interact with the JPS database. This allows an enterprise to customize specific areas of the JPS as needed. This post builds on the first article in the series: Fetching Data From the JSS using API calls & Python. We’ll discuss writing data to the JPS, creating XML data structures, and handling HTTP errors that may occur while communicating the server.

These articles are based on an internal inventory application that began as an experiment I described in a previous post: Using GIF images in Python GUI scripts. Since that post was written the small experiment turned into a cross-platform, automated, text- and GUI-based application that talks to our JPS, campus LDAP as well as multiple MySQL databases. The core activity of the application is maintaining the User and Location area of the Inventory pane for each computer in the JPS.

Lets talk about Representational state transfer or REST for a moment. The API in the JPS is a RESTful API and so clients use specially crafted HTTP URLs to communicate with the server. We use the following verbs to signal the server how the URL should be interpreted:

Verb Definition
GET Retrieve infomation
PUT Write information
POST Create new entry
DELETE Remove information


We previously used the GET command, now we’ll be looking at PUT.

The API commands are typically provided as a hierarchy of information, from broad to granular. For example, you can request information regarding everything the JPS knows about a specific computer or only the contents of the hardware pane for the computer.

While working with the API the most important documentation resource available to you is already in your JPS. You access it through your browser at this address: https://your.jps.location:8443/api/. This page shows you the types of interactions available, the proper format and data returned for each command.


Screen Shot 2016-07-21 at 11.17.04 AM


Writing data to the Jamf Pro server

The API can speak JSON or XML, sometimes a specific call will only support one format, sometimes both. The API documentation will show which formats it supports. The JPS only accepts XML data uploads. The two formats are conceptually similar, and Python provides robust modules to deal with each. We covered working with JSON in Python in the previous article, so we’ll take a close look at XML.

Here is a link to the documentation for Python’s built in XML library.


XML, or Extensible Markup Language, is the document markup language that is the basis of HTML. For our purposes, it consists of tags and attributes. Tags declare the type of data wrapped in an opening and closing block.

Roughly described, tags are general ideas or sections of an item and they can be identified by the following notation:

Attributes can be thought of as the details of an item or its value. Examples of attributes are:


<position>Lady of Bear Island</position>

Here is a textual representation of an valid XML data structure we could send to the JPS:


Here is a graphical representation of the XML document:

xml graphical_colorThe tags and values we’ll be using are mapped to the panes and fields in the various JPS windows. In the example above we’re working on a specific computer, in the User and Location, General and Extension Attribute panes.

Here is the python representation of the above examples:

Let’s examine this code a little closer.

top  is the root of the document. The XML data structure will take care of the opening and closing tags, with the single variable assignment. Notice the SubElement  declarations. We’re telling the new object that it belongs to a specific branch of the tree were building (see the graphical representation above). And then we’re setting the text  value of the object to match our requirements.

ext_attrs  is the variable for the Extension Attribute pane. ext_attr  is single member of the pane. We then proceed to declare and assign value to the required parts of the EA. It’s id  (17), name  (Inventory Purpose), type  (String) and value  (the contents of the inventory_purpose  variable).

general  applies to the General pane. Here you can see we’re only interested in modifying the Asset Tag field, so we don’t need to reference the rest of the items in the pane.

Finally we address the User and Location pane. The API shortens this to location . We continue to declare tags and assign values to them.

PUT-ing to the Jamf Pro server

Now that we’ve created the XML object, we need to send it to the JPS. Here’s is the code we’ll use create the request, add the required features, submit it and display the reply from the server.

Let’s take a closer look at this code.

opener  is the structure that will be used to send the URL to the server.

request  is the actual URL and we’ll continue to build up the object with additional information. computer_url  contains the server address and the location within the API of the computer commands. data  is the XML data structure we created.

base64string  is the encoded form the the user and password we’ll be using the authenticate with.

The add_header  methods are adding additional flags, Authorization  and Content-Type .

get_method  is telling the API how to interpret the URL. In this case, PUT  tells the API we want to modify existing data. Similarly, we would use POST  for new computer entries for example.  actually sends the URL to the API and stores its return communication in the response  variable, which we then print.

Handling HTTP errors

Since we can never assume that some kind of error won’t occur, we have wrapped all of our communication with the JPS in a try except block. This type of control flow allows the script to continue executing instead of coming to a complete halt if some type of error occurs. It proceeds by interpreting the statements in the try  portion until the execution completes or an error occurs. If an error occurs, the code will branch into the except  block. Here’s the code describing our except block, we’ll examine it closely below:

There are three sections to this except block: HTTP errors, URL errors, and any other type of error.

When an HTTP error occurs communicating the JPS, an urllib2.HTTPError  will occur. This type of error could be caused by an incorrect password, incorrect access permissions or a conflict with the data you’re attempting to manipulate. The conflicts can occur when data meant to be unique has occurred in more than one computer. We address this event in

urllib2.URLError  is likely caused by the inability to communicate the the server. A typo in the server address, perhaps.

The final bare except  is a catchall for any other type of error not accounted for in the previous excepts.

A sample script

Here is a sample script that combines all the topics we’ve discussed. It will asked the user a series of questions and post the results to your JPS inside the record for the computer you run the code from. We’ve commented out the code relating to the extension attribute, since the values are unlikely to match those on your server.


Handling constants defined on your JPS

Screen Shot 2016-07-21 at 10.52.28 AM

There are a three special cases of data customized by you on your JPS: Buildings, Departments and Sites. These categories are defined in Management Settings:Network Organization. If you attempt to submit data outside the scope of the specifics you provided, the JPS will return a 409: resource conflict error. Below is a function that requests a JSON object containing the members of the category you specify and builds a list the could then be presented in your user interface. If the category is empty, a list containing a single item, None , is returned.


Without the following resources, my project wouldn’t have progressed as quickly as I did.

No Comments

Leave a Reply