Introduction to JPS API Wrapper: All-in-one package for using the Jamf Pro Server API

As a developer or anyone that writes code, managing APIs can be a time-consuming task. You need to a lot of time figuring out how to send requests, retrieve data, and process responses. In this context, API wrappers come to the rescue.

An API wrapper is a package that simplifies the use of APIs by providing a unified interface for sending and receiving data. In this article, we will be discussing jps-api-wrapper, a Python package for the Jamf Classic API and Jamf Pro API.

What is JPS (Jamf Pro Server) API Wrapper?

JPS API Wrapper is a Python package that simplifies the use of the Jamf Classic API and Jamf Pro API. It provides a unified interface for sending and receiving data, making it easier for developers to interact with the Jamf APIs. With JPS API Wrapper you can easily retrieve data on devices, software, and other assets managed by Jamf, and even perform actions such as sending MDM commands to computers and devices.

The Classic and Pro portions of the API are split into two distinct parts due to the difference how data is returned in each and the support of XML in Classic. Retrieving data from Classic and then using that data in Pro could result in errors due to differences in key values in the JSON data. One example of this would be how Jamf Classic API returns the extension attribute name FileVault - Key Viewed as “FileVault___Key_Viewed” While the Jamf Pro API returns the same value as “FileVault - Key Viewed.“

Getting started with JPS API Wrapper

Installation:

To get started withJPS API Wrapper, you first need to install it. You can install it using pip:

pip install jps-api-wrapper

A Note on Secret Usage:

Before we import and use JPS API Wrapper we’ll go over a quick aside on storing secrets for use later. I recommend storing any sensitive information in an environment variable so that it is not present in your code. We’ll be using the variable JPS_PASSWORD to authenticate, to export this into your environment on macOS or Linux use the following terminal commands (the first command makes it so any following commands with leading spaces are ignored in history):

setopt HIST_IGNORE_SPACE export JPS_USERNAME=yourUsername export JPS_PASSWORD=yourPassword

For Windows you can use the following command in CMD instead:

set JPS_USERNAME=yourUsername set JPS_PASSWORD=yourPassword

One final note is that the setopt HIST_IGNORE_SPACE command is only active for the current session. If you want to have this option permanently you can add the line to your ~/.zshrc or ~/.bashrc file.

Authentication:

Once you have installed the package, you can import it into your Python code and start using it.

We’ll also need to set some variables for authentication in the next step. We will be using the python module “os” to retrieve our environment variables that we set earlier.

Now that we have everything imported and our variables set we can authenticate with our JPS to start using jps-api-wrapper. Behind the scenes we are using the jamf-api-auth module to authenticate with JPS and refresh and invalidate the resulting tokens as needed. We will be using the with statement because whenever Python leaves the with statement jps-api-wrapper is configured to automatically invalidate the token. This covers situations when your program may end before a manual invalidation of the token.

Here is the above code in one continuous file:

And for using Pro you can do the same but substituting Classic with Pro:

Now we’re all set to start using jps-api-wrapper to make some API calls! Let’s look at some examples.

Classic example

This is one of the most basic uses but is something that is commonly used at the start of a script to begin working on the assets.

The pprint module allows us to print out the returned json data in a more human readable way. the output of the above should be something like this (Some data omitted with “…”):

Now we have some data to work with, let’s do something with it. I’m going to get the IDs of all devices with the model identifier “iPad7,11” and send an inventory update command to them.

Let’s break down these new sections and explain what’s going on:

This code snippet is essentially creating a new list of IDs of all mobile devices that have a model identifier of "iPad7,11".

The expression mobile_device["id"] is the value that is being added to the new list. The for clause for mobile_device in mobile_devices["mobile_devices"] is looping through the list of mobile devices, where mobile_devices is a dictionary containing a key named "mobile_devices" which holds the list of mobile devices. The if clause if mobile_device["model_identifier"] == "iPad7,11" is checking if the current mobile device's model identifier is equal to "iPad7,11". If this condition is true, then the mobile device's ID is added to the new list "ipad_711_ids".

This section is creating and queueing an Update Inventory mobile device command for each of the IDs listed in ipad_711_ids. The first parameter in classic.create_mobile_device_command is the MDM command to send, the second parameter is a list of IDs of the mobile devices to send the command to.

Note:

If you’re ever unsure of how a method like classic.create_mobile_device_command works take a look at its docstring for more information on it's functionality.

Pro Example

Lets say that your organization is branching out of your home town of Eau Claire, WI and you want to set all your existing buildings locations as being in the city of Eau Claire, the state of WI, and ZIP code of 54701 before adding your new buildings. You can accomplish that with the following:

Let’s break down the parts of this script now:

First we authenticate with the Pro module and then get all building records. The [“results“] at the end of method is stating that we only want the building record list and not the extraneous information that is returned like building count. We get a list of dictionaries that look like this:

This section iterates through the buildings and sets the values of the stateProvince, zipPostalCode, and city dictionary keys. After this we update the values that we got in the first step.

We iterate through our updated list of buildings and send an update for each one. The first parameters for the pro.update_building is the JSON data including all the information about the building to update, the second parameter is the ID of the building which we get by using building["id"].

All of your existing buildings should now reflect this changes in your JPS!

Note:

Updating and creating assets in the Classic and Pro modules require that you follow the formats defined in the Jamf API Reference. If any such case the docstrings will have links to the endpoints documentation in the Jamf API reference.

Conclusion

JPS API Wrapper is a simple and convenient way to interact with the Jamf Classic API and Jamf Pro API. With its unified interface and easy-to-use methods, it saves developers time and effort when working with Jamf APIs. Whether you are a beginner or an experienced developer, JPS API Wrapper is a great way to use the Jamf API!

For the full documentation (including a list of every method with documentation) please view my ReadTheDocs page for JPS API Wrapper.

For more examples on the JPS API Wrapper being used in real projects that I use every day please check out our public repos on our organization’s GitLab.

If you want to help contribute to the project I accept pull requests, just read the Contributing section of the JPS API Wrapper README before creating a pull request. I plan on keeping the project up to date with with current Jamf Pro Server releases.