Last active
November 18, 2024 12:54
-
-
Save karpadiem/133f1ffe6ba62b2a8ca33aff840bd384 to your computer and use it in GitHub Desktop.
This script is used to audit Mosyle MDM's user and devices license. It queries their API for devices and calculates the amount of licenses an account should be charged for. Using this we found that we were being billed for an extra User Enrollment license for a device and user that no longer showed in our dashboard.
This file contains hidden or 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
| # Author: Jake Garrison | |
| # Date: 2023-03-29 | |
| # Description: This script fetches devices from the Mosyle API and processes it to help us in creating our managment fee reports. It can also be used to audit Mosyle licenses to compaire with current billing. | |
| # This script requires the Mosyle API token, Mosyle API URL, Mosyle API User, and Mosyle API Password to be set. It also requires the tabulate module to be installed. You can install it with pip install tabulate (or pip3 install tabulate) | |
| # THis is a simplified version of our script primarly aimed at helping other companies perform a license audit. | |
| import base64 | |
| import requests | |
| import json | |
| from sys import exit | |
| from tabulate import tabulate | |
| mosyleApiToken = 'xxxxxxxxxxxxxxxx' | |
| mosyleAPIUrl = 'https://businessapi.mosyle.com/v1' #change this if you are using a different EDU instead of business | |
| mosyleAPIUser = 'email@domain.com' #admin user to authinicate with | |
| mosyleAPIPassword = 'xxxxxxxxxxxx' #password of the admin user above | |
| class Mosyle: | |
| # Create Mosyle instance | |
| def __init__(self, key, url = "https://businessapi.mosyle.com/v1", user = "", password = ""): | |
| # Attribute the variable to the instance | |
| self.url = url | |
| self.request = requests.Session() | |
| self.request.headers["accesstoken"] = key | |
| #base64 encode username and password for basic auth | |
| userpass = user + ':' + password | |
| encoded_u = base64.b64encode(userpass.encode()).decode() | |
| self.request.headers["Authorization"] = "Basic %s" % encoded_u | |
| # Create variables requests | |
| def list(self, os): | |
| print("fetching devices of type:", os) | |
| params = { | |
| "operation": "list", | |
| "options": { | |
| "os": os | |
| } | |
| } | |
| # Concatanate url and send the request | |
| return self.request.post(self.url + "/devices", json = params ) | |
| def listTimestamp(self, start, end, os): | |
| params = { | |
| "operation": "list", | |
| "options": { | |
| "os": os, | |
| "enrolldate_start": start, | |
| "enrolldate_end": end | |
| } | |
| } | |
| return self.request.post(self.url + "/devices", json = params ) | |
| def listmobile(self): | |
| params = { | |
| "operation": "list", | |
| "options": { | |
| "os": "ios" | |
| } | |
| } | |
| return self.request.post(self.url + "/devices", json = params ) | |
| def listuser(self, iduser): | |
| params = { | |
| "operation": "list_users", | |
| } | |
| if iduser is not None: | |
| params["options"] = { | |
| "id": iduser | |
| } | |
| return self.request.post(self.url + "/users", json = params ) | |
| def setAssetTag(self, serialnumber, tag): | |
| params = { | |
| "operation": "update_device", | |
| "serialnumber": serialnumber, | |
| "asset_tag": tag | |
| } | |
| return self.request.post(self.url + "/devices", json = params ) | |
| # Set the token for the Mosyle Api | |
| mosyle = Mosyle(mosyleApiToken, mosyleAPIUrl, mosyleAPIUser, mosyleAPIPassword) | |
| #device types to get from mosyle. AppleTV devices are not needed for this report as they are free and do not count towards the license count | |
| deviceTypes = ['mac', 'ios'] | |
| allUsers = mosyle.listuser(None).json() | |
| allUsers = allUsers['response'][0]['users'] | |
| totlaLicenses = {'device' : 0, 'user' : 0, 'total' : 0} | |
| print(tabulate(allUsers)) | |
| def checkUserAndReturn(allUsers, userid): | |
| #check all users for 'identifier' key with value of userid | |
| for user in allUsers: | |
| if user['identifier'] == userid: | |
| return user | |
| #if we get here, we didn't find the user, get it from mosyle | |
| userInfo = mosyle.listuser(userid).json() | |
| return userInfo['response'][0]['users'][0] | |
| #list of devices | |
| devices = [] | |
| #list of of users set up with user enrollment | |
| UEusers = {} | |
| #loop through the device types and get the devices | |
| for deviceType in deviceTypes: | |
| #get all devices from mosyle | |
| devices_responses = mosyle_response = mosyle.list(deviceType).json() | |
| if 'status' in mosyle_response: | |
| if mosyle_response['status'] != "OK": | |
| print('There was an issue with the Mosyle API. Stopping.', mosyle_response['message']) | |
| exit(); | |
| if 'status' in mosyle_response['response'][0]: | |
| print('There was an issue with the Mosyle API. Stopping script.') | |
| print(mosyle_response['response'][0]['info']) | |
| exit() | |
| #clean up the device information and put it in a list | |
| for device_response in devices_responses['response'][0]['devices']: | |
| #if the device does not have a serial number, it is a user enrolled device | |
| if device_response['serial_number'] == None: | |
| #this is a user enrolled device. Check if the UEusers dictionary has a userid key with mac_response['userid']. If not, add them and increase set their mac count to 1, ios count to 0. If they are, increase their mac count by 1 | |
| if device_response['userid'] not in UEusers: | |
| userInfo = checkUserAndReturn(allUsers, device_response['userid']) | |
| email = userInfo['email'] | |
| #get the domain from the email address | |
| UEusers[device_response['userid']] = { | |
| 'userid': device_response['userid'], | |
| 'mac_count': 0, | |
| 'ios_count': 0 | |
| } | |
| #increase the user enrollment license count | |
| totlaLicenses['user'] += 1 | |
| #increase the total license count | |
| totlaLicenses['total'] += 1 | |
| #if the device is an ios device, increase the ios count by 1 | |
| if device_response['os'] == 'ios': | |
| UEusers[device_response['userid']]['ios_count'] += 1 | |
| if device_response['os'] == 'mac': | |
| UEusers[device_response['userid']]['mac_count'] += 1 | |
| else: | |
| #this is a device enrolled mac. Add it to the macs list | |
| #take the first 3 characters of mac_response['device_name'] and determain what company it belongs to | |
| #check if the response has an userid key, if not, set it to None | |
| if 'userid' not in device_response: | |
| device_response['userid'] = None | |
| devices.append({ | |
| 'serial': device_response['serial_number'], | |
| 'device_type': device_response['os'], | |
| 'asset_tag': device_response['asset_tag'], | |
| 'name': device_response['device_name'], | |
| 'user': device_response['userid'] | |
| }) | |
| #increase the device license count | |
| totlaLicenses['device'] += 1 | |
| #increase the total license count | |
| totlaLicenses['total'] += 1 | |
| #print the devices that count towards the device license count | |
| print(tabulate(devices, headers="keys")) | |
| table_data = [[user, data['mac_count'], data['ios_count'],(data['mac_count'] + data['ios_count'])] for user, data in UEusers.items()] | |
| #print the users that count towards the user license count | |
| print(tabulate(table_data, headers=['User ID', 'BYOD Mac', 'BYOB iOS', 'Total Devices'])) | |
| #print the total licenses in a table | |
| print(tabulate([['Device', totlaLicenses['device']], ['User', totlaLicenses['user']], ['Total', totlaLicenses['total']]], headers=['License Type', 'Count'])) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment