Created
October 10, 2023 00:27
-
-
Save t-lark/0c1dc014a22035e61bd073fa48dd82f9 to your computer and use it in GitHub Desktop.
Un-manage devices in Jamf via Classic API
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/opt/snowflake/bin/python3 | |
""" | |
In Jamf Pro version 10.49 and higher, Jamf has removed the ability to mass edit management account settings | |
from a saved search results page via mass actions. This means that you either have to un-manage old devices by manually | |
un-managing them one by one in their device record, or write some sort of API script. You can also technically delete | |
the device record if that is an acceptable use case at your Org. | |
This script will take the JSS ID from a smart group, grab the ID of each device and store it in a Python list. Then it will | |
iterate through that list and send Classic API commands to remove the management account thus freeing up the license. | |
This code was used to free up licenses of unused devices recently, but | |
see this for more in-depth details: | |
https://derflounder.wordpress.com/2023/08/15/updating-management-status-in-jamf-pro-computer-inventory-records-on-jamf-pro-10-49-0-and-later/ | |
Use this feature request to ask for this feature back: | |
https://ideas.jamf.com/ideas/JN-I-27551 | |
""" | |
# import modules | |
import requests | |
import json | |
import base64 | |
# global vars to interact with the classic API | |
# ensure these variables match your environment | |
# main API endpoint for your smart group with the criteria to remove from management | |
# ensure you put your smart group ID in this string below | |
JSS_URL = "https://your-jamf-server.com/JSSResource/computergroups/id/<id>" | |
# input your API creds | |
JSS_API_USER = "your-api-username" | |
JSS_API_PASSWD = "your-api-user-passwd" | |
# template URL for individual device records | |
JSS_DEVICE_URL = "https://your-jamf-server.com/JSSResource/computers/id/" | |
# start functions | |
def get_smart_group_results(): | |
"""function to get the JSS ID from a smart group""" | |
# vars for headers | |
creds = JSS_API_USER + ":" + JSS_API_PASSWD | |
creds = creds.encode() | |
b64_creds = base64.b64encode(creds) | |
# define the headers for the request | |
headers = { | |
"Accept": "application/json", | |
"Content-Type": "application/json", | |
"Authorization": "Basic " + b64_creds.decode("utf-8"), | |
} | |
# make the request | |
r = requests.get(JSS_URL, headers=headers) | |
# ensure results are JSON encoded data | |
results = json.loads(r.text) | |
return results | |
def build_device_list(data): | |
"""simple function to parse the JSON return of Jamf Classic API""" | |
# start with a blank list | |
device_list = [] | |
# iterate through our JSON data, starting at the list of dicts under the "computers" key | |
for i in range(len(data["computer_group"]["computers"])): | |
# we will use jamf ID for the post that is all we need for the device record | |
jss_id = data["computer_group"]["computers"][i]["id"] | |
# append the list of jss ids | |
device_list.append(jss_id) | |
return device_list | |
def post_un_mange_cmd(jss_id_list): | |
"""function to generate the XML payload to remove management for that device""" | |
# get the xml payload to remove management from jamf, thus unlicensing retired systems | |
xml_playload = "<computer><general><remote_management><managed>false</managed></remote_management></general></computer>" | |
# generate basic creds for API user, encode them, add headers, the required stuff | |
creds = JSS_API_USER + ":" + JSS_API_PASSWD | |
creds = creds.encode() | |
b64_creds = base64.b64encode(creds) | |
headers = { | |
"Accept": "application/json", | |
"Content-Type": "application/xml", | |
"Authorization": "Basic " + b64_creds.decode("utf-8"), | |
} | |
# loop through the ID list, and send our XML payload to each device record by JSS ID | |
for id in jss_id_list: | |
# ensure the JSS ID is a string data type as you cannot concat string to integer data types | |
url = JSS_DEVICE_URL + str(id) | |
# build the request and send it with the XML payload | |
r = requests.put(url, data=xml_playload, headers=headers) | |
# print results to stdout | |
print(f"un-managing device ID {id} with status code {r.status_code}") | |
def main(): | |
"""main function to run the jewels""" | |
# get the smart group data | |
sg_data = get_smart_group_results() | |
# filter out JSS IDs of devices | |
device_list = build_device_list(sg_data) | |
# send un-manage payloads to all device IDs in our list | |
post_un_mange_cmd(device_list) | |
if __name__ == "__main__": | |
"""call the main policy""" | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This doesn't have good error handling at all (nor is it really good code by any means) but this was a quick and dirty script written to just get this done. Therefore all output goes to
stdout
and you will see thissuccesses:
failures will return a non 20x (non 200) status