Skip to content

Working with Raw Data

RIchard Glaser edited this page Nov 13, 2023 · 12 revisions

Deprecated - Attention

Please be advised that this wiki page is deprecated. We have transitioned python-jamf and jctl to the jps-api-wrapper project for records interaction. Using jps-api-wrapper, you can efficiently obtain information about devices, software, and other assets managed by Jamf Pro. Moreover, you can execute various actions, including sending MDM commands to computers and devices.

The information on the following wiki page will be retained for historical purposes.

Working with Raw Data

There are no limitations when working with the raw data. The API object has get, post, put, and delete methods. They interact with the Jamf Pro server using the get, post, put, and delete HTTP methods. The API methods return raw data stored as Python dictionaries, arrays, and strings. To see all of the Jamf API endpoints that you can work with, go to the following URL on your server. https://example.com:8443/api

If you would like to try examples on this page, start a python REPL. You should know Python before trying these examples. If you don't know Python and you want to use jamf, please check out jctl.

python3

Import pprint and jamf and access the api object.

from pprint import pprint
import jamf
api = jamf.API()

The API class is a singleton. So if you call jamf.API() over and over and it will always return the same object. The reason behind this is so that you can access the api and any cached data from anywhere that imports the jamf module. However, the API class does not cache the data, the records class does. There might be room to improve this in the future (i.e. making the API not a singleton).

The downside of the API class being a singleton is that you can not talk to multiple Jamf servers.

Getting Data

The API get method downloads the associated data from the Jamf Pro server. If you store it in a variable, it does not update itself. If you make changes on the server, you’ll need to run the API get method again to get the updated data.

Get any information from your Jamf Pro server using the classic API endpoints. This includes nested dictionaries.

pprint(api.get('accounts'))
pprint(api.get('buildings'))
pprint(api.get('categories'))
pprint(api.get('computergroups'))
pprint(api.get('computers'))
pprint(api.get('departments'))
pprint(api.get('licensedsoftware'))
pprint(api.get('networksegments'))
pprint(api.get('osxconfigurationprofiles'))
pprint(api.get('packages'))
pprint(api.get('patches'))
pprint(api.get('policies'))
pprint(api.get('scripts'))

All Categories

Get all categories and work with the nested dictionaries

categories = api.get('categories')['categories']['category']
category_names = [x['name'] for x in categories]
print(f"first category: {category_names[0]}")
pprint(category_names)

Computer Management Information

This example demonstrates using an id in the get request:

computers = api.get('computers')['computers']['computer']
pprint(computers[0])
pprint(api.get(f"computermanagement/id/{computers[0]['id']}"))
pprint(api.get(f"computermanagement/id/{computers[0]['id']}/subset/general"))

Computer Groups with Filtering

Getting computer groups and filtering using list comprehension filtering.

computergroups = api.get('computergroups')['computer_groups']['computer_group']
smartcomputergroups = [i for i in computergroups if i['is_smart'] == 'true']
pprint(smartcomputergroups)
staticcomputergroups = [i for i in computergroups if i['is_smart'] != 'true']
pprint(staticcomputergroups)
computergroupids = [i['id'] for i in computergroups]
pprint(computergroupids)

Posting Data

When creating a new data, in this case a static computer group, the id in the url ("1") is ignored and the next available id is used. The name in the url ("ignored") is also ignored and the name in the data ("realname") is what is actually used with the static computer group.

import json
api.post("computergroups/id/1",json.loads( '{"computer_group": {"name": "test", "is_smart": "false", "site": {"id": "-1", "name": "None"}, "criteria": {"size": "0"}, "computers": {"size": "0"}}}' ))
api.post("computergroups/name/ignored",json.loads( '{"computer_group": {"name": "realname", "is_smart": "false", "site": {"id": "-1", "name": "None"}, "criteria": {"size": "0"}, "computers": {"size": "0"}}}' ))

Updating Data

The following code changes the group "realname" created in the example above to "new name".

import json
api.put("computergroups/name/realname",json.loads( '{"computer_group": {"name": "new name", "is_smart": "false", "site": {"id": "-1", "name": "None"}, "criteria": {"size": "0"}, "computers": {"size": "0"}}}' ))
computergroups = api.get('computergroups')['computer_groups']['computer_group']
newgroup = [i for i in computergroups if i['name'] == 'new name']

And this is how to change the computer group name using the id.

api.put(f"computergroups/id/{newgroup[0]['id']}",json.loads( '{"computer_group": {"name": "newer name", "is_smart": "false", "site": {"id": "-1", "name": "None"}, "criteria": {"size": "0"}, "computers": {"size": "0"}}}' ))

Deleting Data

The following example deletes the two groups we just created above:

api.delete("computergroups/name/test")
api.delete(f"computergroups/id/{newgroup[0]['id']}")

Updating Data En Masse

This is where the real power of jamf comes in.

Update Polices with Custom Triggers

The following example searches all policies for the custom trigger "update_later" and replaces it with "update_now".

#!/usr/bin/env python3

import jamf

api = jamf.API()
all_policies = api.get('policies')['policies']['policy']
for policy_hook in all_policies:
    policy = api.get(f"policies/id/{policy_hook['id']}")
    name = policy['policy']['general']['name']
    custom_trigger = policy['policy']['general']['trigger_other']
    print(f"Working on {name}")
    if (custom_trigger == "update_later"):
        policy['policy']['general']['trigger_other'] = "update_now"
        api.put(f"policies/id/{policy_hook['id']}", policy)
        print(f"Changed custom trigger from {custom_trigger} to update_now")

Update Policies with Custom Triggers via File

The next example prints out the code you'd need to enter into a python3 interpreter to set the custom_triggers. Save the output of this script to a file, then edit the file with the custom triggers you want for each item. Delete the items you don't want to change.

#!/usr/bin/env python3

import jamf

api = jamf.API()
all_policies = api.get('policies')['policies']['policy']

print("""#!/usr/bin/env python3

import jamf

api = jamf.API()
""")

for policy_hook in all_policies:
    policy = api.get(f"policies/id/{policy_hook['id']}")
    custom_trigger = policy['policy']['general']['trigger_other']
    print(f"print(f\"Working on {policy['policy']['general']['name']}\")\n"
          f"policy = api.get(\"policies/id/{policy_hook['id']}\")\n"
          f"policy['policy']['general']['trigger_other'] = "
          f"\"{policy['policy']['general']['trigger_other']}\"\n"
          f"api.put(\"policies/id/{policy_hook['id']}\", policy)\n\n")

Create Custom Triggers Script

Save the script named "custom_triggers_1.py" with the following commands:

./custom_triggers_1.py > custom_triggers_2.py
chmod 755 custom_triggers_2.py

Edit Custom Triggers Script

Then edit "custom_triggers_2.py" with the custom triggers you want (and remove what you don't want to modify).

Then run "custom_triggers_2.py" script to make modifications.