Using the JAMF Pro API and Python to detect duplicated attributes

Using the JAMF Pro API and Python to detect duplicated attributes

64193274No matter where you go, there they are…

We’ve been attempting to develop a unified inventory solution for all of our computers using JAMF Pro. While building out our database, we conducted a large number of tests and mistakes were made while adding machines. These errors resulted in duplicate and incomplete entries scattered among the legitimate machines. Compounding this was the discovery that the Window’s computers we were adding had motherboards with identical information (UUIDs and serial numbers, for example) making it difficult or impossible for the JAMF database to distinguish one machine from another.

Unfortunately, the JAMF’s Windows client tools don’t offer the ability to define an alternate UUID. If you’ve experienced this issue in your deployments, we invite you to visit and vote for our feature request here: Add flag to jamf.exe to specify UUID of my choosing during enrollment. This issue required us to use the JAMF API and script a solution to add these machines with software-defined UUIDs.

During our cleanup of the database we discovered that while the machine may have been successfully added to the database, it may have additional database fields that duplicated other machines information and cause HTTP 409 errors when attempting to modify their records using the API. A 409 error denotes a resource conflict, usually caused by multiple machines sharing an identical value for a field which should hold a unique value. At this time the JAMF Pro server does not offer much in its UI to track down these duplicates. But by combining the JAMF Pro API and the power of Python, we can quickly find these offending entries.

This article builds on topics covered in Fetching Data From the JSS using API calls & Python. We have additional related posts coming soon that look at searching and writing data to the JAMF Pro database using Python.

Python’s dictionaries

This script makes good use of Python’s dictionaries or associative arrays. A dictionary is a list of key and value pairs. Each key-value entry has a single key. This singular key can be any of a number of different data types. In the image below, the keys are book title, or strings. They could just as easily be phone numbers or dates, for example.

The value portion of the pair can be any kind and quantity of data. In the case of the example image, authors. While you normally wouldn’t change the content of the key, changing the value field is a very powerful feature.



This script stores the attribute (computer name, serial number, etc) as the key, and the value is a list of the clients that share that value. For example, if we have a machine with a computer name “MacBook Pro”, we’ll store the name as the key and the first client ID is stored as the value. The next time we find a machine named “MacBook Pro”, we’ll add it’s ID to the list of IDs stored in the value field.

Breaking down the code

In broad strokes here is how the script works:

  1. Ask the JAMF server for the list of computers and their identifiers.
  2. For each client, ask the JAMF server for specific pieces of information.
  3. Define a dictionary for each of those pieces.
  4. For each client and each dictionary do the following:
    • Does this piece of information exist in the dictionary?
    • If it does, add the client identifier to the value for that piece.
    • If it doesn’t, create a new pair in the dictionary with the piece as the key and the client identifier as the value.
  5. Print out a summary for each dictionary by checking each key for values with more than one client ID.

Let’s take a closer look at the major sections of the overall script.

The computer object

This is an object that we can pass back and forth between functions. It stores the variables that we will compare later. The __init__  section is called when the object is created and sets the initial state of the variables. The print_myself function is not used as in the script and is left for future usage.

Grabbing the clients

This function asks the JAMF Pro API to return the client identifiers and computer names of all of the machines stored in the JAMF database. This information is then reformatted into a data structure we can use later to ask the JAMF server about specific machines.

Inspecting individual clients

This function requests more information about individual machines. The API call we are using returns the least amount of extra data beyond what we need. Much of the function involves handling potential errors that could occur when interacting with the JAMF Pro API and smoothly handling those events. The last few lines of the function store the actual values we’re interested in.

Building dictionaries and handling collisions

This section is the heart of the script. We request the list of machines and specific individual info, and then check for collisions. Each of the separate try/except blocks checks for a different type of attribute. Try/excepts work in the following way: if an error occurs in the try block, execution of the script moves to the associated except block. In our script, if we attempt to access a preexisting  attribute key, we append it to the existing value list, otherwise the key doesn’t exist and we encounter an error switch to the except block and create a new key-value pair.

Displaying the results

Here we display the results of the script. This is done by checking each dictionary for items with more than one identifier in its value and printing those items.


Here is a sample of the output you can expect from the full script. I’ve broken down each section below. I’ve also edited the content with ellipsis’ (‘…’) for brevity. Using the client identifiers you can begin to address the impact they have on your deployment and actions that need to be taken to correct the issue.

1 – Scanning clients
Here the script notes each client as it is parsed.

2 – Summary
Shows the total number of clients in the JAMF database.

3 – Serial number collisions
This sections shows where duplicate serial numbers have been found. The output is read as follows: number of clients, the matching value, and a list of the JAMF client IDs sharing the same value. The u'value'  format means the script is printing a raw, unformatted unicode string, the quote marks denote the beginning and end of the string. This will allow use to see the difference between an empty string, like this one, and a string consisting of a single space.

Take a close look at the line showing 336 duplicates:

[336] u'' [1326, 301, 378, 1397, 783, 791, 796, 798, ...]

This line is actually showing that there are 336 clients with no serial number at all.

The following line shows 3 clients with Not Available as the serial number.

The 4 following lines with Apple-like serial numbers are likely indicating duplicated machines.

4 – UDID collisions
The UDID is the used by the JAMF server as the primary means of differentiating between clients. It will not allow multiple identical values.

5 – MAC collisions
Media access controller (MAC) address collisions. This is the unique address for the primary ethernet port.

6 – Alternate MAC collisions
Alternate (secondary) MAC address collisions, if the machine has multiple ethernet ports.

7 – Computer name collisions
Duplicated computer names will be listed here.

The complete script

To make the script operable you will need to provide the address the your JAMF Pro server.

Additional resources

Here are links to sites offering more in-depth discussions of Python’s dictionaries.

No Comments

Post A Comment