├── API-PackageCreator.sh ├── AddComputers_Static_API.py ├── AddComputers_Static_API.sh ├── CreateBuildingFromCSV.sh ├── DeleteAbandonedUsers.sh ├── DeleteComputersBySerial.sh ├── PromptUserForEmailAddress.sh ├── README.md ├── clearAllComputerLocations.py ├── createUserFromCSV.sh ├── delete_by_id.sh ├── managementAccount.sh ├── mdGroupEnroll.py ├── mobile_app_icon_fixer.sh ├── removeMobileDeviceInvitations.sh ├── thunderboltDisplayAPI.sh ├── updateDeviceInventory.py └── updateDeviceInventoryBySmartGroup.py /API-PackageCreator.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ######################################################################################################## 4 | # 5 | # Copyright (c) 2013, JAMF Software, LLC. All rights reserved. 6 | # 7 | # Redistribution and use in source and binary forms, with or without 8 | # modification, are permitted provided that the following conditions are met: 9 | # * Redistributions of source code must retain the above copyright 10 | # notice, this list of conditions and the following disclaimer. 11 | # * Redistributions in binary form must reproduce the above copyright 12 | # notice, this list of conditions and the following disclaimer in the 13 | # documentation and/or other materials provided with the distribution. 14 | # * Neither the name of the JAMF Software, LLC nor the 15 | # names of its contributors may be used to endorse or promote products 16 | # derived from this software without specific prior written permission. 17 | # 18 | # THIS SOFTWARE IS PROVIDED BY JAMF SOFTWARE, LLC "AS IS" AND ANY 19 | # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | # DISCLAIMED. IN NO EVENT SHALL JAMF SOFTWARE, LLC BE LIABLE FOR ANY 22 | # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23 | # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 | # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 25 | # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 27 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | # 29 | #################################################################################################### 30 | 31 | #Import Packages via the API for direct upload of the flat .pkg or .dmg to the root JDS 32 | 33 | #Variables to be set for the old server 34 | server="" #Server name of the old server e.g. "JAMFSoftware.com" 35 | username="" #JSS username with API privileges 36 | password="" #Password for the JSS account 37 | packageName="" #Name of the package (including file extension) 38 | 39 | curl -k -v -u ${username}:${password} -H "Content-Type: text/xml" https://${server}:8443/JSSResource/packages/id/id -d "${packageName}${packageName}" -X POST 40 | 41 | exit 0 42 | -------------------------------------------------------------------------------- /AddComputers_Static_API.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | import StringIO 4 | import csv 5 | from urllib2 import urlopen, URLError, HTTPError, Request 6 | import base64 7 | 8 | #path to CSV file 9 | csvfile='/Users/Shared/computers.csv' 10 | #Use full REST url for JSS URL 11 | #Example: jssurl='https://jss.company.com:8443/JSSResource/computergroups/id/7' 12 | jssurl='' 13 | 14 | jssuser='' 15 | jsspass='' 16 | 17 | #open the csv 18 | with open (csvfile, 'r') as myfile: 19 | data=myfile.read().replace('\n', '') 20 | 21 | #create a list from the csv 22 | f = StringIO.StringIO(data) 23 | reader = csv.reader(f, delimiter=',') 24 | 25 | #format the data from list into a xml 26 | csvdata = '\n' 27 | for row in reader: 28 | for w in row: 29 | csvdata += '' + w + '\n' 30 | 31 | csvdata += '' 32 | 33 | #print out contents as an error check 34 | print('contents of data:\n' + csvdata) 35 | 36 | #submit to the JSS API 37 | req = Request(jssurl, data=csvdata) 38 | req.add_header('Content-Type', 'text/xml') 39 | req.get_method = lambda: 'PUT' 40 | base64string = base64.encodestring(jssuser+':'+jsspass)[:-1] 41 | authheader = "Basic %s" % base64string 42 | req.add_header("Authorization", authheader) 43 | xmldata = urlopen(req) 44 | 45 | #print result 46 | print(xmldata) 47 | -------------------------------------------------------------------------------- /AddComputers_Static_API.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | #Enter in the name of the file path that we want to add to a static group 4 | csvFilePath="" 5 | 6 | #Uncomment your data stored in the CSV 7 | #dataPref="Mac" 8 | #dataPref="JSSID" 9 | dataPref="Name" 10 | 11 | #Enter in the ID of the static group we want to add the computer 12 | groupID="" 13 | 14 | #Enter in the URL of the JSS we are are pulling and pushing the data to. (NOTE: We will need the https:// and :8443. EX:https://jss.company.com:8443 ) 15 | jssURL="" 16 | 17 | #Enter in a username and password that has the correct permissions to the JSS API for what data we need 18 | jssUser="" 19 | jssPass="" 20 | 21 | #Default temp file name and path we will build for API submission 22 | groupFilePath="/tmp/computerGroup.xml" 23 | 24 | 25 | #####No configuration variables below this line. You should not need to edit unless you are modifying functionality 26 | 27 | #We will use these variable to build our xml file 28 | a="" 29 | b="" 30 | c="" 31 | d="" 32 | 33 | 34 | #Build our array of values 35 | echo "Building the array from CSV..." 36 | v=`cat $csvFilePath` 37 | PREV_IFS="$IFS" # Save previous IFS 38 | IFS=, values=($v) 39 | IFS="$PREV_IFS" # Restore IFS 40 | 41 | #Build the XML from the array 42 | echo "Building the xml at $groupFilePath..." 43 | echo "$a" > "$groupFilePath" 44 | for val in "${values[@]}" 45 | do 46 | echo "$b" >> "$groupFilePath" 47 | case $dataPref in 48 | "Mac") 49 | echo "$val" >> "$groupFilePath" 50 | ;; 51 | "JSSID") 52 | echo "$val" >> "$groupFilePath" 53 | ;; 54 | "Name") 55 | echo "$val" >> "$groupFilePath" 56 | ;; 57 | *) 58 | echo "error: no preference of CSV type was specified... Quitting..." 59 | exit 1 60 | ;; 61 | esac 62 | echo "$c" >> "$groupFilePath" 63 | done 64 | echo "$d" >> "$groupFilePath" 65 | 66 | #Submit group to JSS 67 | echo "File submitting to $jssURL..." 68 | curl -k -v -u $jssUser:$jssPass $jssURL/JSSResource/computergroups/id/$groupID -T "$groupFilePath" -X PUT 69 | 70 | #Clean up 71 | rm "$groupFilePath" 72 | 73 | 74 | 75 | -------------------------------------------------------------------------------- /CreateBuildingFromCSV.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ######################################################################################################## 4 | # 5 | # Copyright (c) 2015, JAMF Software, LLC. All rights reserved. 6 | # 7 | # Redistribution and use in source and binary forms, with or without 8 | # modification, are permitted provided that the following conditions are met: 9 | # * Redistributions of source code must retain the above copyright 10 | # notice, this list of conditions and the following disclaimer. 11 | # * Redistributions in binary form must reproduce the above copyright 12 | # notice, this list of conditions and the following disclaimer in the 13 | # documentation and/or other materials provided with the distribution. 14 | # * Neither the name of the JAMF Software, LLC nor the 15 | # names of its contributors may be used to endorse or promote products 16 | # derived from this software without specific prior written permission. 17 | # 18 | # THIS SOFTWARE IS PROVIDED BY JAMF SOFTWARE, LLC "AS IS" AND ANY 19 | # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | # DISCLAIMED. IN NO EVENT SHALL JAMF SOFTWARE, LLC BE LIABLE FOR ANY 22 | # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23 | # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 | # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 25 | # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 27 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | # 29 | #################################################################################################### 30 | # 31 | # DESCRIPTION 32 | # 33 | # This script reads a CSV of buildings and imports them via the JSS API. This script 34 | # expects each building name to be on it's own line within the CSV file. 35 | # 36 | # 37 | #################################################################################################### 38 | 39 | #Declare variables 40 | server="yourJSS.jamfcloud.com" #Server name and port 41 | username="admin" #JSS username with API privileges 42 | password="password" #Password for the JSS account 43 | file="/Users/admin/Desktop/Buildings.csv" #Path to CSV 44 | 45 | #Do not modify below this line 46 | 47 | #Loop through the building names and submit to the JSS until we've reached the end of the CSV 48 | while IFS= read -r line || [ -n "$line" ]; do 49 | echo "${line}" 50 | curl -u ${username}:${password} -H "Content-Type: text/xml" https://${server}/JSSResource/buildings/id/0 -d "${line}" -X POST 51 | done < $file 52 | 53 | exit 0 54 | -------------------------------------------------------------------------------- /DeleteAbandonedUsers.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Script to delete users that are not associated with computers, devices, or VPP assignments 3 | 4 | read -p "JSS URL: " jssURL 5 | read -p "JSS Username: " jssUser 6 | read -s -p "JSS Password: " jssPass 7 | echo "" # secret flag fails to newline 8 | 9 | index="0" 10 | users=() 11 | 12 | #Get a list of Users 13 | IDs=`curl -k -u $jssUser:$jssPass ${jssURL}/JSSResource/users -X GET` 14 | size=`echo $IDs | xpath //users/size | sed 's/<[^>]*>//g'` 15 | echo "IDs: $IDs" 16 | 17 | echo $size " user objects will be scanned." 18 | 19 | #Put the IDs into an array 20 | while [ $index -lt ${size} ] 21 | do 22 | index=$[$index+1] 23 | users+=(`echo $IDs | xpath //users/user[${index}]/id | sed 's/<[^>]*>//g'`) 24 | done 25 | 26 | 27 | # Sort through each user ID individually and grep for IDs in computer, device, and vpp links 28 | for i in "${users[@]}" 29 | do 30 | echo "Checking on ${i}" 31 | user=`curl -k -u $jssUser:$jssPass ${jssURL}/JSSResource/users/id/${i} -X GET` 32 | computers=`echo $user | xpath //user/links/computers | grep id` 33 | devices=`echo $user | xpath //user/links/mobile_devices | grep id` 34 | vpp=`echo $user | xpath //user/links/vpp_assignments | grep id` 35 | # Delete users that meet our criteria 36 | if [[ -z "$computers" && -z "$devices" && -z "$vpp" ]] ; then 37 | echo "Deleting ${i}" 38 | curl -k -v -u $jssUser:$jssPass ${jssURL}/JSSResource/users/id/${i} -X DELETE 39 | fi 40 | done 41 | -------------------------------------------------------------------------------- /DeleteComputersBySerial.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | ######################################################################################################## 4 | # 5 | # Copyright (c) 2014, JAMF Software, LLC. All rights reserved. 6 | # 7 | # Redistribution and use in source and binary forms, with or without 8 | # modification, are permitted provided that the following conditions are met: 9 | # * Redistributions of source code must retain the above copyright 10 | # notice, this list of conditions and the following disclaimer. 11 | # * Redistributions in binary form must reproduce the above copyright 12 | # notice, this list of conditions and the following disclaimer in the 13 | # documentation and/or other materials provided with the distribution. 14 | # * Neither the name of the JAMF Software, LLC nor the 15 | # names of its contributors may be used to endorse or promote products 16 | # derived from this software without specific prior written permission. 17 | # 18 | # THIS SOFTWARE IS PROVIDED BY JAMF SOFTWARE, LLC "AS IS" AND ANY 19 | # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | # DISCLAIMED. IN NO EVENT SHALL JAMF SOFTWARE, LLC BE LIABLE FOR ANY 22 | # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23 | # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 | # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 25 | # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 27 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | # 29 | #################################################################################################### 30 | # 31 | # DESCRIPTION 32 | # 33 | # This script deletes computer records from the JSS by their serial number based 34 | # on input from a delimited (by newline or comma) text file containing only serial numbers. 35 | # 36 | #################################################################################################### 37 | # 38 | # HISTORY 39 | # 40 | # Version 1.0 41 | # Created by Nick Anderson, JAMF Software, LLC, on May 28, 2014 42 | # 43 | #################################################################################################### 44 | 45 | # User input 46 | read -p "JSS URL (HTTPS Only): " server 47 | read -p "JSS Username: " username 48 | read -s -p "JSS Password: " password 49 | echo "" 50 | read -p "Data File Path (can be dragged into terminal): " input 51 | 52 | # Reformat our input to csv 53 | touch /tmp/serials.csv 54 | cat $input | tr '\n' ',' > /tmp/serials.csv 55 | file="/tmp/serials.csv" 56 | 57 | # Count entries in file 58 | count=`cat ${file} | awk -F, '{print NF}'` 59 | 60 | # Start the count 61 | index="0" 62 | 63 | # Loop through the entries and send a deletion to the JSS for each of them 64 | while [ $index -lt ${count} ] 65 | do 66 | index=$[$index+1] 67 | var=`cat ${file} | awk -F, '{print $'"${index}"'}'` 68 | 69 | curl -v -k -u $username:$password $server/JSSResource/computers/serialnumber/$var -X DELETE 70 | done 71 | 72 | # Clean up 73 | rm /tmp/serials.csv 74 | 75 | exit 0 76 | -------------------------------------------------------------------------------- /PromptUserForEmailAddress.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -x 2 | 3 | jssurl="" # example: https://example.com:8443 4 | username="" 5 | password="" 6 | 7 | ############################### 8 | # Do not edit below this line # 9 | ############################### 10 | 11 | # Grab our serial number so we can edit the right computer record 12 | serialnumber=`ioreg -l | awk '/IOPlatformSerialNumber/ { print $4;}' | sed 's/"//' | sed 's/"//'` 13 | 14 | # Make our temp files 15 | touch /tmp/setemail.xml 16 | touch /tmp/setemail2.xml 17 | 18 | # Set our email address by prompting the user for input with applescript 19 | email=`/usr/bin/osascript < /tmp/setemail.xml 28 | 29 | 30 | # Set the email address 31 | cat /tmp/setemail.xml | sed 's/.*<\/email_address>//' | sed "s//$email<\/email_address>/g" > /tmp/setemail2.xml 32 | 33 | # Submit our new user and location information to the JSS 34 | curl -k -u $username:$password $jssurl/JSSResource/computers/serialnumber/$serialnumber/subset/location -T "/tmp/setemail2.xml" -X PUT 35 | 36 | 37 | rm /tmp/setemail.xml 38 | rm /tmp/setemail2.xml 39 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | API_Scripts 2 | =========== 3 | 4 | Copyright (c) 2013, JAMF Software, LLC. All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | * Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | * Redistributions in binary form must reproduce the above copyright 11 | notice, this list of conditions and the following disclaimer in the 12 | documentation and/or other materials provided with the distribution. 13 | * Neither the name of the JAMF Software, LLC nor the 14 | names of its contributors may be used to endorse or promote products 15 | derived from this software without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY JAMF SOFTWARE, LLC "AS IS" AND ANY 18 | EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 19 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 | DISCLAIMED. IN NO EVENT SHALL JAMF SOFTWARE, LLC BE LIABLE FOR ANY 21 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 22 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 23 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 24 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 26 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | 28 | 29 | -------------------------------------------------------------------------------- /clearAllComputerLocations.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | #################################################################################################### 4 | # 5 | # Copyright (c) 2016, JAMF Software, LLC. All rights reserved. 6 | # 7 | # Redistribution and use in source and binary forms, with or without 8 | # modification, are permitted provided that the following conditions are met: 9 | # * Redistributions of source code must retain the above copyright 10 | # notice, this list of conditions and the following disclaimer. 11 | # * Redistributions in binary form must reproduce the above copyright 12 | # notice, this list of conditions and the following disclaimer in the 13 | # documentation and/or other materials provided with the distribution. 14 | # * Neither the name of the JAMF Software, LLC nor the 15 | # names of its contributors may be used to endorse or promote products 16 | # derived from this software without specific prior written permission. 17 | # 18 | # THIS SOFTWARE IS PROVIDED BY JAMF SOFTWARE, LLC "AS IS" AND ANY 19 | # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | # DISCLAIMED. IN NO EVENT SHALL JAMF SOFTWARE, LLC BE LIABLE FOR ANY 22 | # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23 | # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 | # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 25 | # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 27 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | # 29 | #################################################################################################### 30 | # 31 | # SUPPORT FOR THIS PROGRAM 32 | # 33 | # This program is distributed "as is" by JAMF Software, LLC. 34 | # 35 | #################################################################################################### 36 | # 37 | # ABOUT THIS PROGRAM 38 | # 39 | # NAME 40 | # clearAllComputerLocations.py -- Update Computer Locatoins 41 | # 42 | # SYNOPSIS 43 | # /usr/bin/python clearAllComputerLocations.py 44 | # 45 | # DESCRIPTION 46 | # This script was designed to update all Computer locations in a JSS. 47 | # 48 | # For the script to function properly, users must be running the JSS version 7.31 or later and 49 | # the account provided must have API privileges to "READ" and "UPDATE" mobile devices in the JSS. 50 | # 51 | #################################################################################################### 52 | # 53 | # HISTORY 54 | # 55 | # Version: 1.0 56 | # 57 | # - Created by Bram Cohen on May 12, 2016 58 | # 59 | ##################################################################################################### 60 | # 61 | # DEFINE VARIABLES & READ IN PARAMETERS 62 | # 63 | ##################################################################################################### 64 | # 65 | # HARDCODED VALUES SET HERE 66 | # 67 | jss_host = "" #Example: 127.0.0.1 if run on server 68 | jss_port = 8443 69 | jss_path = "" #Example: "jss" for a JSS at https://www.company.com:8443/jss 70 | jss_username = "admin" 71 | jss_password = "password" 72 | device_xml = "" 73 | 74 | 75 | ##DONT EDIT BELOW THIS LINE 76 | import ssl 77 | import json 78 | import httplib 79 | import base64 80 | import urllib2 81 | import socket 82 | 83 | ##Computer Object Definition 84 | class Device: 85 | id = -1 86 | 87 | ##Check variable 88 | def verifyVariable(name, value): 89 | if value == "": 90 | print "Error: Please specify a value for variable \"" + name + "\"" 91 | sys.exit(1) 92 | 93 | ## the main function. 94 | def main(): 95 | verifyVariable("jss_host",jss_host) 96 | verifyVariable("jss_port",jss_port) 97 | verifyVariable("jss_username",jss_username) 98 | verifyVariable("jss_password",jss_password) 99 | devices=grabDeviceIDs() 100 | updateDeviceInventory(devices) 101 | 102 | ##Grab and parse the mobile devices and return them in an array. 103 | def grabDeviceIDs(): 104 | devices=[]; 105 | ## parse the list 106 | for deviceListJSON in (getDeviceListFromJSS()["computers"]): 107 | d = Device() 108 | d.id = deviceListJSON.get("id") 109 | devices.append(d) 110 | print "Found " + str(len(devices)) + " devices." 111 | return devices 112 | 113 | class TLS1Connection(httplib.HTTPSConnection): 114 | def __init__(self, host, **kwargs): 115 | httplib.HTTPSConnection.__init__(self, host, **kwargs) 116 | 117 | def connect(self): 118 | sock = socket.create_connection((self.host, self.port), self.timeout, self.source_address) 119 | if getattr(self, '_tunnel_host', None): 120 | self.sock = sock 121 | self._tunnel() 122 | 123 | self.sock = ssl.wrap_socket(sock, self.key_file, self.cert_file, ssl_version=ssl.PROTOCOL_TLSv1) 124 | 125 | 126 | class TLS1Handler(urllib2.HTTPSHandler): 127 | def __init__(self): 128 | urllib2.HTTPSHandler.__init__(self) 129 | 130 | def https_open(self, req): 131 | return self.do_open(TLS1Connection, req) 132 | 133 | 134 | ##Create a header for the request 135 | def getAuthHeader(u,p): 136 | # Compute base64 representation of the authentication token. 137 | token = base64.b64encode('%s:%s' % (u,p)) 138 | return "Basic %s" % token 139 | 140 | ##Download a list of all mobile devices from the JSS API 141 | def getDeviceListFromJSS(): 142 | print "Getting device list from the JSS..." 143 | opener = urllib2.build_opener(TLS1Handler()) 144 | request = urllib2.Request("https://" + str(jss_host) + ":" + str(jss_port) + str(jss_path) + "/JSSResource/computers") 145 | 146 | request.add_header("Authorization", "Basic " + base64.b64encode('%s:%s' % (jss_username,jss_password))) 147 | request.add_header("Accept", "application/json") 148 | request.get_method = lambda: 'GET' 149 | 150 | try: 151 | data = opener.open(request) 152 | return json.load(data) 153 | except httplib.HTTPException as inst: 154 | print "Exception: %s" % inst 155 | sys.exit(1) 156 | except ValueError as inst: 157 | print "Exception decoding JSON: %s" % inst 158 | sys.exit(1) 159 | 160 | ##Submit the command to update a device's inventory to the JSS 161 | def updateDeviceInventory(devices): 162 | print "Updating Computer Location..." 163 | ##Parse through each device and submit the command to update inventory 164 | for index, device in enumerate(devices): 165 | percent = "%.2f" % (float(index) / float(len(devices)) * 100) 166 | print str(percent) + "% Complete -" 167 | submitDataToJSS(device) 168 | print "100.00% Complete" 169 | 170 | ##Update data for a single device 171 | def submitDataToJSS(Device): 172 | print "\tSubmitting command to update device id " + str(Device.id) + "..." 173 | try: 174 | url = "https://" + str(jss_host) + ":" + str(jss_port) + str(jss_path) + "/JSSResource/computers/id/" + str(Device.id) 175 | #Write out the XML string with new data to be submitted 176 | newDataString = "" + str(device_xml) 177 | #print "Data Sent: " + newDataString 178 | opener = urllib2.build_opener(TLS1Handler()) 179 | request = urllib2.Request(url,newDataString) 180 | request.add_header("Authorization", getAuthHeader(jss_username,jss_password)) 181 | request.add_header('Content-Type', 'application/xml') 182 | request.get_method = lambda: 'PUT' 183 | opener.open(request) 184 | except httplib.HTTPException as inst: 185 | print "\tException: %s" % inst 186 | except ValueError as inst: 187 | print "\tException submitting PUT XML: %s" % inst 188 | except urllib2.HTTPError as inst: 189 | print "\tException submitting PUT XML: %s" % inst 190 | except: 191 | print "\tUnknown error submitting PUT XML." 192 | 193 | ## Code starts executing here. Just call main. 194 | main() 195 | -------------------------------------------------------------------------------- /createUserFromCSV.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ######################################################################################################## 4 | # 5 | # Copyright (c) 2014, JAMF Software, LLC. All rights reserved. 6 | # 7 | # Redistribution and use in source and binary forms, with or without 8 | # modification, are permitted provided that the following conditions are met: 9 | # * Redistributions of source code must retain the above copyright 10 | # notice, this list of conditions and the following disclaimer. 11 | # * Redistributions in binary form must reproduce the above copyright 12 | # notice, this list of conditions and the following disclaimer in the 13 | # documentation and/or other materials provided with the distribution. 14 | # * Neither the name of the JAMF Software, LLC nor the 15 | # names of its contributors may be used to endorse or promote products 16 | # derived from this software without specific prior written permission. 17 | # 18 | # THIS SOFTWARE IS PROVIDED BY JAMF SOFTWARE, LLC "AS IS" AND ANY 19 | # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | # DISCLAIMED. IN NO EVENT SHALL JAMF SOFTWARE, LLC BE LIABLE FOR ANY 22 | # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23 | # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 | # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 25 | # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 27 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | # 29 | #################################################################################################### 30 | # 31 | # DESCRIPTION 32 | # 33 | # This script reads a CSV of usernames and email addresses and imports them to the JSS. 34 | # The format must be username in the first column and email address in the second 35 | # 36 | #################################################################################################### 37 | # 38 | # HISTORY 39 | # 40 | # -Created by Sam Fortuna, JAMF Software, LLC, on June 18th, 2014 41 | # -Updated by Sam Fortuna, JAMF Software, LLC, on August 5th, 2014 42 | # -Improved error handling 43 | # -Returns a list of failed submissions 44 | # -Updated by TJ Bauer, JAMF Software, LLC, on September 9th, 2014 45 | # -Improved reading of file 46 | # 47 | #################################################################################################### 48 | 49 | #Variables 50 | file="/path/to/file.csv" #Path to the CSV 51 | server="your.jss.com" #Server name 52 | username="admin" #JSS username with API privileges 53 | password="password" #Password for the JSS account 54 | 55 | #Option to read in the path from Terminal 56 | if [[ "$file" == "" ]]; then 57 | echo "Please enter the path to the CSV" 58 | read file 59 | fi 60 | 61 | #Verify we can read the file 62 | data=`cat $file` 63 | if [[ "$data" == "" ]]; then 64 | echo "Unable to read the file path specified" 65 | echo "Ensure there are no spaces and that the path is correct" 66 | exit 1 67 | fi 68 | 69 | #Set a counter for the loop 70 | counter="0" 71 | 72 | duplicates=[] 73 | #Loop through the CSV and submit data to the API 74 | while read name 75 | do 76 | counter=$[$counter+1] 77 | line=`echo "$data" | head -n $counter | tail -n 1` 78 | user=`echo "$line" | awk -F , '{print $1}'` 79 | email=`echo "$line" | awk -F , '{print $2}'` 80 | 81 | echo "Attempting to create user - $user : $email" 82 | 83 | #Construct the XML 84 | apiData="${user}${email}" 85 | output=`curl -k -u $username:$password -H "Content-Type: text/xml" https://$server:8443/JSSResource/users/id/0 -d "$apiData" -X POST` 86 | 87 | #Error Checking 88 | error="" 89 | error=`echo $output | grep "Conflict"` 90 | if [[ $error != "" ]]; then 91 | duplicates+=($user) 92 | fi 93 | done < $file 94 | 95 | echo "The following users could not be created:" 96 | printf -- '%s\n' "${duplicates[@]}" 97 | 98 | exit 0 99 | -------------------------------------------------------------------------------- /delete_by_id.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | ######################################################################################################## 4 | # 5 | # Copyright (c) 2017, JAMF Software, LLC. All rights reserved. 6 | # 7 | # Redistribution and use in source and binary forms, with or without 8 | # modification, are permitted provided that the following conditions are met: 9 | # * Redistributions of source code must retain the above copyright 10 | # notice, this list of conditions and the following disclaimer. 11 | # * Redistributions in binary form must reproduce the above copyright 12 | # notice, this list of conditions and the following disclaimer in the 13 | # documentation and/or other materials provided with the distribution. 14 | # * Neither the name of the JAMF Software, LLC nor the 15 | # names of its contributors may be used to endorse or promote products 16 | # derived from this software without specific prior written permission. 17 | # 18 | # THIS SOFTWARE IS PROVIDED BY JAMF SOFTWARE, LLC "AS IS" AND ANY 19 | # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | # DISCLAIMED. IN NO EVENT SHALL JAMF SOFTWARE, LLC BE LIABLE FOR ANY 22 | # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23 | # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 | # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 25 | # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 27 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | # 29 | #################################################################################################### 30 | # 31 | # DESCRIPTION 32 | # 33 | # This script deletes computer records from the JSS by their ID based 34 | # on input from a delimited (by newline) text file. 35 | # 36 | #################################################################################################### 37 | # 38 | # HISTORY 39 | # 40 | # Version 1.0 41 | # Created by Nick Anderson, JAMF Software, LLC, on May 28, 2014 42 | # Version 1.1 43 | # Fixed to work on Computers and using ID's rather than names 44 | # Edit by Bram Cohen, JAMF Software, LLC, on November 30, 2017 45 | # 46 | #################################################################################################### 47 | 48 | # User input 49 | read -p "JSS URL (HTTPS Only): " server 50 | read -p "JSS Username: " username 51 | read -s -p "JSS Password: " password 52 | echo "" 53 | read -p "Data File Path (can be dragged into terminal): " input 54 | 55 | # Reformat our input 56 | touch /tmp/ids.csv 57 | cat $input | tr '\n' ',' > /tmp/ids.csv 58 | file="/tmp/ids.csv" 59 | 60 | # Count entries in file 61 | count=`cat ${file} | awk -F, '{print NF}'` 62 | 63 | # Start the count 64 | index="0" 65 | 66 | # Loop through the entries and send a deletion to the JSS for each of them 67 | while [ $index -lt ${count} ] 68 | do 69 | index=$[$index+1] 70 | var=`cat ${file} | awk -F, '{print $'"${index}"'}'` 71 | 72 | curl -v -k -u $username:$password $server/JSSResource/computers/id/$var -X DELETE 73 | done 74 | 75 | # Clean up 76 | rm /tmp/ids.csv 77 | 78 | exit 0 79 | -------------------------------------------------------------------------------- /managementAccount.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | #Based on code by mm2270 https://jamfnation.jamfsoftware.com/discussion.html?id=7575 3 | #Slightly modified else clause to re-check for en1 MAC by Bram Cohen 4 | 5 | 6 | apiURL="https://your.casper.jss:8443/JSSResource/computers/macaddress/" 7 | apiUser="apiusername" 8 | apiPass="apipassword" 9 | MacAdd=$( networksetup -getmacaddress en0 | awk '{ print $3 }' | sed 's/:/./g' ) 10 | 11 | ManAccount=$( curl -s -u $apiUser:$apiPass "$apiURL$MacAdd" | xpath /computer/general/remote_management/management_username[1] | sed 's///;s/<\/management_username>//' ) 12 | if [[ "$ManAccount" != "" ]]; then 13 | echo "$ManAccount" 14 | else 15 | MacAdd=$( networksetup -getmacaddress en1 | awk '{ print $3 }' | sed 's/:/./g' ) 16 | ManAccount=$( curl -s -u $apiUser:$apiPass "$apiURL$MacAdd" | xpath /computer/general/remote_management/management_username[1] | sed 's///;s/<\/management_username>//' ) 17 | if [[ "$ManAccount" != "" ]]; then 18 | echo "$ManAccount" 19 | fi 20 | fi 21 | -------------------------------------------------------------------------------- /mdGroupEnroll.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python # Limitations: # - Only one device per student is supported # - Classes may not contain a + or / character as the API cannot parse these at this time # Settings jssAddr = "https://yourjssurl:8443" # JSS server address with or without https:// and port jssUser = "usernameHere" # JSS login username (API privileges must be set) jssPass = "passwordHere" # JSS login password csvPath = "/path/to/studentCourse.csv" # Path and file name for the input CSV file # Imports from urllib2 import urlopen, URLError, HTTPError, Request from xml.dom import minidom from xml.sax import make_parser, ContentHandler from sys import exc_info, argv, stdout, stdin from getpass import getpass import base64 import csv import re import ssl def main(): global jssAddr global jssUser global jssPass global csvPath updateMDGroups = JSSUpdateMobileDeviceGroups() studentList = updateMDGroups.getCSVData(csvPath) updateMDGroups.updateGroups(jssAddr, jssUser, jssPass, studentList) class JSSUpdateMobileDeviceGroups: def __init__(self): self.numMobileDevicesUpdated = 0 self.deviceMap = dict() self.unmanagedDeviceMap = dict() def getCSVData(self, csvPath): # Read in CSV csvinput = open(csvPath) reader = csv.reader(csvinput) return reader def updateGroups(self, jssAddr, jssUser, jssPass, studentList): # Cache mobile device ID to student username mapping self.grabMobileDeviceData(jssAddr, jssUser, jssPass) # Assign student device to the classes (groups) url = jssAddr + "/JSSResource/mobiledevicegroups" grabber = CasperGroupPUTHandler(jssUser, jssPass) for studentLine in studentList: if studentLine[0] != "Student ID": self.handleStudentDeviceAssignment(grabber, url, studentLine) print "Successfully updated %d devices in the JSS." % self.numMobileDevicesUpdated def grabMobileDeviceData(self, jssAddr, jssUser, jssPass): url = jssAddr + "/JSSResource/mobiledevices" grabber = CasperDeviceXMLGrabber(jssUser, jssPass) grabber.parseXML(url, CasperMobileDeviceListHandler(grabber, self.deviceMap, self.unmanagedDeviceMap)) def handleStudentDeviceAssignment(self, grabber, url, studentLine): # Create mobile device studentLine XML... if studentLine[0] in self.deviceMap: if "/" in studentLine[1] or "+" in studentLine[1]: print "Error: User: %s, Class: %s, Error: Class contains forward slash or ends in plus character" % (studentLine[0], studentLine[1]) else: studentDeviceID = self.deviceMap[studentLine[0]] newGroupAssignment = self.createNewGroupElement(studentDeviceID, studentLine[1]) self.handleGroupPUT(grabber, url, studentLine[1], newGroupAssignment) else: print "Error: User: %s, Class: %s, Error: Could not find a mobile device match for student username or device is unmanaged" % (studentLine[0], studentLine[1]) def handleGroupPUT(self, grabber, url, className, newGroupAssignment): # PUT new XML apiClassURLRAW = url + "/name/" + className apiClassURL = apiClassURLRAW.replace (' ', '+') apiClassName = className.replace ('+', '') apiClassName = apiClassName.replace (' ', '+') ###########UNCOMMENT NEXT TWO LINES FOR DEBUG MODE############# #print "PUT-ing URL %s: " % (apiClassURL) #print newGroupAssignment.toprettyxml() putStatus = grabber.openXMLStream("%s/name/%s" % (url, apiClassName), newGroupAssignment) if putStatus is None: self.numMobileDevicesUpdated += 1 def createNewGroupElement(self, studentDeviceID, groupName): global eventValues newGroupAssignment = minidom.Document() group = self.appendEmptyElement(newGroupAssignment, newGroupAssignment, "mobile_device_group") self.appendNewTextElement(newGroupAssignment, group, "is_smart", "false") self.appendNewTextElement(newGroupAssignment, group, "name", groupName) groupAdditions = self.appendEmptyElement(newGroupAssignment, group, "mobile_device_additions") deviceElement = self.appendEmptyElement(newGroupAssignment, groupAdditions, "mobile_device") self.appendNewTextElement(newGroupAssignment, deviceElement, "id", studentDeviceID) return newGroupAssignment def appendEmptyElement(self, doc, section, newElementTag): newElement = doc.createElement(newElementTag) section.appendChild(newElement) return newElement def appendNewTextElement(self, doc, section, newElementTag, newElementValue): newElement = self.appendEmptyElement(doc, section, newElementTag) newValueElement = doc.createTextNode(newElementValue) newElement.appendChild(newValueElement) return newElement class CasperDeviceXMLGrabber: def __init__(self, jssUser, jssPass): self.jssUser = jssUser self.jssPass = jssPass def parseXML(self, url, handler): req = Request(url) base64string = base64.encodestring('%s:%s' % (self.jssUser, self.jssPass))[:-1] authheader = "Basic %s" % base64string req.add_header("Authorization", authheader) try: gcontext = ssl._create_unverified_context() MobileDeviceList = urlopen(req, context=gcontext) except (URLError, HTTPError) as urlError: # Catch errors related to urlopen() print "Error when opening URL: " + urlError.__str__() exit(1) except: # Catch any unexpected problems and bail out of the program print "Unexpected error:", exc_info()[0] exit(1) parser = make_parser() parser.setContentHandler(handler) parser.parse(MobileDeviceList) class CasperGroupPUTHandler: def __init__(self, jssUser, jssPass): self.jssUser = jssUser self.jssPass = jssPass def parseXML(self, url): return self.openXMLStream(url, None) def openXMLStream(self, url, xmlout): try: if xmlout is None: req = Request(url) else: req = Request(url, data=xmlout.toxml()) req.add_header('Content-Type', 'text/xml') req.get_method = lambda: 'PUT' base64string = base64.encodestring('%s:%s' % (self.jssUser, self.jssPass))[:-1] authheader = "Basic %s" % base64string req.add_header("Authorization", authheader) gcontext = ssl._create_unverified_context() xmldata = urlopen(req, context=gcontext) if xmlout is None: xmldoc = minidom.parse(xmldata) else: xmldoc = None return xmldoc except (URLError, HTTPError) as urlError: # Catch errors related to urlopen() if urlError.code == 404: if xmlout is None: req = Request(url) else: req = Request(url, data=xmlout.toxml()) req.add_header('Content-Type', 'text/xml') req.get_method = lambda: 'POST' base64string = base64.encodestring('%s:%s' % (self.jssUser, self.jssPass))[:-1] authheader = "Basic %s" % base64string req.add_header("Authorization", authheader) gcontext = ssl._create_unverified_context() xmldata = urlopen(req, context=gcontext) if xmlout is None: xmldoc = minidom.parse(xmldata) else: xmldoc = None return xmldoc print "XML Data: %s" % (xmlout.toxml()) print "Error when opening URL %s - %s " % (url, urlError.__str__()) return "Error" except: # Catch any unexpected problems and bail out of the program print "Unexpected error:", exc_info()[0] exit(1) # This class is used to parse the /mobiledevices list to get all of the ids and usernames class CasperMobileDeviceListHandler(ContentHandler): def __init__(self, grabber, deviceMap, unmanagedDeviceMap): ContentHandler.__init__(self) self.grabber = grabber self.deviceMap = deviceMap self.unmanagedDeviceMap = unmanagedDeviceMap self.currentID = "" self.inID = False self.inSize = False self.inUsername = False self.inManaged = False def startElement(self, tag, attributes): if tag == "id": self.inID = True elif tag == "username": self.inUsername = True elif tag == "managed": self.inManaged = True elif tag == "size": self.inSize = True def endElement(self, tag): self.inID = False self.inSize = False self.inManaged = False self.inUsername = False if tag == "mobiledevices": print "Finished collecting mobile devices for lookup" def characters(self, data): if self.inID: self.currentID = data elif self.inUsername: if data != "" and self.currentID not in self.unmanagedDeviceMap.values(): self.deviceMap[data] = self.currentID elif self.inManaged: if data != "true": self.unmanagedDeviceMap[data] = self.currentID elif self.inSize: self.numDevices = data print "Collecting data for " + data + " Mobile Device(s)..." if __name__ == "__main__": main() -------------------------------------------------------------------------------- /mobile_app_icon_fixer.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | #################################################################################################### 4 | # 5 | # Copyright (c) 2015, JAMF Software, LLC. All rights reserved. 6 | # 7 | # Redistribution and use in source and binary forms, with or without 8 | # modification, are permitted provided that the following conditions are met: 9 | # * Redistributions of source code must retain the above copyright 10 | # notice, this list of conditions and the following disclaimer. 11 | # * Redistributions in binary form must reproduce the above copyright 12 | # notice, this list of conditions and the following disclaimer in the 13 | # documentation and/or other materials provided with the distribution. 14 | # * Neither the name of the JAMF Software, LLC nor the 15 | # names of its contributors may be used to endorse or promote products 16 | # derived from this software without specific prior written permission. 17 | # 18 | # THIS SOFTWARE IS PROVIDED BY JAMF SOFTWARE, LLC "AS IS" AND ANY 19 | # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | # DISCLAIMED. IN NO EVENT SHALL JAMF SOFTWARE, LLC BE LIABLE FOR ANY 22 | # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23 | # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 | # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 25 | # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 27 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | # 29 | #################################################################################################### 30 | # 31 | # General Changelog 32 | # 33 | # v0.0 - General API template created 34 | # Nick Anderson 35 | # v0.1 - Re-aimed all API queries at mobile device applications and stored values 36 | # from returned information 37 | # Katie Davis 38 | # v0.2 - Informative output and appIconID logic added 39 | # Matthew Boyle 40 | # v1.0 - Fixed appIconID logic, added queries to iTunes API to parse for icon ID, download and 41 | # convert icon to base64 and upload icon to the JSS, fixed streamlined credential mode 42 | # Nick Anderson 43 | # 44 | #################################################################################################### 45 | # 46 | # Purpose and Use 47 | # 48 | # The purpose of this script is to find any mobile device applications in the JSS that do not 49 | # have an icon associated with them, and upload an icon for that application. As with any 50 | # script that makes bulk changes to the JSS, it is advised to make a database backup. 51 | # 52 | #################################################################################################### 53 | 54 | if [ -z "$1" ] ; then 55 | # Prompt the user for information to connect to the JSS with 56 | read -p "JSS URL: " jssurl 57 | read -p "JSS Username: " jssuser 58 | read -s -p "JSS Password: " jsspassword 59 | echo "" 60 | else 61 | # Quick testing credentials, run the script with any trailing flag to use this mode 62 | jssurl="https://jss.com:8443" 63 | jssuser="admin" 64 | jsspassword="password" 65 | fi 66 | 67 | 68 | # Set a counter for our 'for' to start at the beginning 69 | index="0" 70 | # Create an array for apps 71 | apps=() 72 | 73 | # Get all of the app records 74 | IDs=`curl -k -u $jssuser:$jsspassword ${jssurl}/JSSResource/mobiledeviceapplications -X GET` 75 | # Record the number of apps to be put into the array from the returned XML 76 | size=`echo $IDs | xpath //mobile_device_applications/size | sed 's/<[^>]*>//g'` 77 | 78 | # Sort the app IDs into an array (using the start point of index=0 and the size variable as the end point) 79 | while [ $index -lt ${size} ] 80 | do 81 | index=$[index+1] 82 | apps+=(`echo $IDs | xpath //mobile_device_applications/mobile_device_application[${index}]/id | sed 's/<[^>]*>//g'`) 83 | done 84 | 85 | for i in "${apps[@]}" 86 | do 87 | # Tell the terminal which inventory record we're working on 88 | echo "$(tput setaf 2)Scanning ${i}$(tput sgr0)" 89 | # Collect the comprehensive inventory information for what we're checking in the array 90 | app=`curl -s -k -u $jssuser:$jsspassword ${jssurl}/JSSResource/mobiledeviceapplications/id/${i} -X GET` 91 | 92 | # Filter the information down to prevent contamination to our greps 93 | #appInfo=`echo $app | xpath //mobile_device_application | sed 's/<[^>]*>//g'` 94 | appName=`echo $app | xpath //mobile_device_application/general/name | sed 's/<[^>]*>//g'` 95 | appVersion=`echo $app | xpath //mobile_device_application/general/version | sed 's/<[^>]*>//g'` 96 | appExternalUrl=`echo $app | xpath //mobile_device_application/general/external_url | sed 's/<[^>]*>//g'` 97 | appIconID=`echo $app | xpath //mobile_device_application/general/icon/id | sed 's/<[^>]*>//g'` 98 | appAdamID=`echo $appExternalUrl | perl -lne 'print $1 if /(^.*)(?=\?)/'` 99 | appAdamID2=`echo $appAdamID | sed 's/[^0-9]*//g'` 100 | 101 | # If the application ID is not found or is lower than zero, 102 | if [[ ! -z $appIconID ]]; then 103 | echo "$(tput setaf 2)${appName} is good...$(tput sgr0)" 104 | else 105 | echo "${appName} | ${appAdamID2} | ${appIconID}" 106 | 107 | # Grab the JSON data from iTunes and filter it down to the icon URL 108 | iconUrl=`curl http://ax.itunes.apple.com/WebObjects/MZStoreServices.woa/wa/wsLookup?id=$appAdamID2 | python -mjson.tool | grep artworkUrl512 | grep -o '['"'"'"][^"'"'"']*['"'"'"]' | tr -d '"'` 109 | 110 | # Curl the icon's URL and convert it to base64 for upload into the JSS 111 | iconData=`curl $iconUrl | openssl base64` 112 | 113 | # Submit our new icon to the JSS # ${iconUrl:(-12)} 114 | curl -s -k -u $jssuser:$jsspassword -H "Content-Type: text/xml" ${jssurl}/JSSResource/mobiledeviceapplications/id/${i} -d "${iconUrl:(-12)}$iconData" -X PUT 115 | 116 | fi 117 | done 118 | -------------------------------------------------------------------------------- /removeMobileDeviceInvitations.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | ########################################################################################################################### 4 | username="admin" 5 | password="abc123" 6 | jssapiurl="https://yourJSSUrl:8443" 7 | ########################################################################################################################### 8 | 9 | 10 | # Download all invitiations from the JSS 11 | echo "Downloading Data from JSS..." 12 | curl -k -u $username:$password $jssapiurl/JSSResource/mobiledeviceinvitations -X GET | xmllint --format - >> /tmp/here.xml 13 | echo "Received XML Data from JSS" 14 | 15 | # Extract all Invitation ID's and save to a temp 16 | echo "Parsing invitations..." 17 | grep '"|cut -f1 -d"<" >> /tmp/ids.txt 18 | invitations=(wc -l /tmp/ids.txt | awk '{print $2}') 19 | echo "Found $invitations invitations!" 20 | 21 | # For all found in tmp, curl a delete command for each 22 | count=0 23 | for line in $(cat /tmp/ids.txt); do 24 | echo "Deleting Invitation $line" 25 | curl -k -u $username:$password $jssapiurl/JSSResource/mobiledeviceinvitations/name/$line -X DELETE 26 | count=($count+1) 27 | done 28 | echo "Deleted $count invitations successfully!" 29 | 30 | # Clean up temp files 31 | rm -f /tmp/here.xml 32 | rm -f /tmp/ids.txt 33 | echo "Clean-up Completed" 34 | -------------------------------------------------------------------------------- /thunderboltDisplayAPI.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | ######################################################################################################## 3 | # 4 | # Copyright (c) 2013, JAMF Software, LLC. All rights reserved. 5 | # 6 | # Redistribution and use in source and binary forms, with or without 7 | # modification, are permitted provided that the following conditions are met: 8 | # * Redistributions of source code must retain the above copyright 9 | # notice, this list of conditions and the following disclaimer. 10 | # * Redistributions in binary form must reproduce the above copyright 11 | # notice, this list of conditions and the following disclaimer in the 12 | # documentation and/or other materials provided with the distribution. 13 | # * Neither the name of the JAMF Software, LLC nor the 14 | # names of its contributors may be used to endorse or promote products 15 | # derived from this software without specific prior written permission. 16 | # 17 | # THIS SOFTWARE IS PROVIDED BY JAMF SOFTWARE, LLC "AS IS" AND ANY 18 | # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 19 | # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 | # DISCLAIMED. IN NO EVENT SHALL JAMF SOFTWARE, LLC BE LIABLE FOR ANY 21 | # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 22 | # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 23 | # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 24 | # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 26 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | # 28 | #################################################################################################### 29 | 30 | # HARDCODED VARIABLES 31 | apiusername="" # Username that has API privileges for 'Peripherals' 32 | apipassword="" # Password for User that has API privileges for 'Peripherals' 33 | jssbase="" # JSS base url e.g. "https://yourJSSurl:8443" 34 | 35 | # CHECK FOR SCRIPT PARAMETERS IN JSS 36 | if [ "$4" != "" ] && [ "$apiusername" == "" ] 37 | then 38 | apiusername="$4" 39 | fi 40 | 41 | if [ "$5" != "" ] && [ "$apipassword" == "" ] 42 | then 43 | apipassword="$5" 44 | fi 45 | 46 | if [ "$6" != "" ] && [ "$jssbase" == "" ] 47 | then 48 | jssbase="$6" 49 | fi 50 | #################################################################################################### 51 | # SCRIPT FUNCTIONS - - DO NOT MODIFY BELOW THIS LINE 52 | #################################################################################################### 53 | 54 | #################################################################################################### 55 | # FUNCTION - Get the serial number of a connected Thunderbolt Display 56 | # $serial = serial number of Thunderbolt Display 57 | #################################################################################################### 58 | function getSerialNumber() { 59 | serial="$(/usr/sbin/system_profiler | egrep "Display Serial Number:" | awk '{print $4}')" 60 | } 61 | 62 | #################################################################################################### 63 | # FUNCTION - Get the Ethernet Interface MAC Addresses and save to variables en0 and en1 64 | # $en0 - MAC address of interface en0 e.g 01.02.03.04.05.06 65 | # $en1 - MAC address of interface en1 e.g 01.02.03.04.05.06 66 | #################################################################################################### 67 | function getInterface() { 68 | # Get interfaces Ethernet Addresses 69 | for iface in `ifconfig -lu` ; do 70 | case $iface in 71 | en0) 72 | en0="$(/sbin/ifconfig en0 | grep ether | awk '{print $NF}' | tr ':' '.')" ;; 73 | en1) 74 | en1="$(/sbin/ifconfig en1 | grep ether | awk '{print $NF}' | tr ':' '.')" ;; 75 | esac 76 | done 77 | } 78 | 79 | #################################################################################################### 80 | # FUNCTION - Get the count of JSS Peripherals and next ID that we would use 81 | # $sizeoutput - current count of JSS Peripherals 82 | # $next - number of the next peripheral that we would need to use 83 | #################################################################################################### 84 | function getNextPeripheral() { 85 | # Find the Next Peripheral ID, if its needed later 86 | sizeoutput="$(/usr/bin/curl -k -u $apiusername:$apipassword $jssbase/JSSResource/peripherals -X GET | sed -n -e 's/.*\(.*\)<\/size>.*/\1/p')" 87 | next=`expr $sizeoutput + 1` 88 | } 89 | 90 | #################################################################################################### 91 | # FUNCTION - Generate an xml file for use by the cURL POST 92 | # $filename - Path to the temp file we are writing xml 93 | #################################################################################################### 94 | function generatePostXMLFile() { 95 | filename="/tmp/Peripheral.xml" 96 | /usr/bin/touch $filename 97 | echo "" >> $filename 98 | echo "" >> $filename 99 | echo " " >> $filename 100 | echo " " >> $filename 101 | echo " Display" >> $filename 102 | echo " " >> $filename 103 | echo " Manufacturer" >> $filename 104 | echo " Apple" >> $filename 105 | echo " " >> $filename 106 | echo " " >> $filename 107 | echo " Serial" >> $filename 108 | echo " $serial" >> $filename 109 | echo " " >> $filename 110 | echo " " >> $filename 111 | echo " " >> $filename 112 | echo " " >> $filename 113 | echo " $computerid" >> $filename 114 | echo " " >> $filename 115 | echo "" >> $filename 116 | /usr/bin/xmllint -o $filename --format $filename 117 | } 118 | 119 | #################################################################################################### 120 | # FUNCTION - Generate an xml file for use by the cURL PUT 121 | # $filename - Path to the temp file we are writing xml 122 | #################################################################################################### 123 | function generatePutXMLFile() { 124 | filename="/tmp/Peripheral.xml" 125 | /usr/bin/touch /tmp/Peripheral.xml 126 | echo "" >> $filename 127 | echo "" >> $filename 128 | echo " " >> $filename 129 | echo " " >> $filename 130 | echo " " >> $filename 131 | echo " Serial" >> $filename 132 | echo " $serial" >> $filename 133 | echo " " >> $filename 134 | echo " " >> $filename 135 | echo " " >> $filename 136 | echo "" >> $filename 137 | /usr/bin/xmllint -o $filename --format $filename 138 | } 139 | #################################################################################################### 140 | # FUNCTION - Process to clean up after ourselves 141 | #################################################################################################### 142 | function deleteXMLFile() { 143 | /bin/rm $filename 144 | } 145 | 146 | #################################################################################################### 147 | # FUNCTION - Get information via the JSSAPI to get the computerid# and peripheral id number 148 | # $computerapioutput - Full API output of current Computer Record in the JSS 149 | # $computerid - The computer ID in the JSS 150 | # $periid - Peripheral serial # in the JSS for this computer 151 | #################################################################################################### 152 | function getJSSInformationen0() { 153 | computerapioutput="$(/usr/bin/curl -k -u $apiusername:$apipassword $jssbase/JSSResource/computers/macaddress/$en0 -X GET)" 154 | computerid="$(echo $computerapioutput | sed -n -e 's/.*\(.*\)<\/id>.*/\1/p')" 155 | periid="$(echo $computerapioutput | sed -n -e 's/.*\(.*\)<\/field_1>.*/\1/p')" 156 | } 157 | 158 | #################################################################################################### 159 | # FUNCTION - Get information via the JSSAPI to get the computerid# and peripheral id number 160 | # $computerapioutput - Full API output of current Computer Record in the JSS 161 | # $computerid - The computer ID in the JSS 162 | # $periid - Peripheral serial # in the JSS for this computer 163 | #################################################################################################### 164 | function getJSSInformationen1() { 165 | computerapioutput="$(/usr/bin/curl -k -u $apiusername:$apipassword $jssbase/JSSResource/computers/macaddress/$en1 -X GET)" 166 | computerid="$(echo $computerapioutput | sed -n -e 's/.*\(.*\)<\/id>.*/\1/p')" 167 | periid="$(echo $computerapioutput | sed -n -e 's/.*\(.*\)<\/field_1>.*/\1/p')" 168 | } 169 | 170 | #################################################################################################### 171 | # FUNCTION - cURL POST of the generatePostXMLFile function 172 | #################################################################################################### 173 | function xmlPost() { 174 | echo "No previous Thunderbolt display found in JSS, Creating record for $serial" 175 | /usr/bin/curl -k -u $apiusername:$apipassword $jssbase/JSSResource/peripherals/id/$next -T "/tmp/Peripheral.xml" -X POST 176 | } 177 | 178 | #################################################################################################### 179 | # FUNCTION - cURL PUT of the generatePutXMLFile function 180 | #################################################################################################### 181 | function xmlPut() { 182 | echo "Different Thunderbolt display found in JSS, updating record to $serial" 183 | /usr/bin/curl -k -u $apiusername:$apipassword $jssbase/JSSResource/peripherals/id/$periid -T "/tmp/Peripheral.xml" -X PUT 184 | } 185 | 186 | #################################################################################################### 187 | # FUNCTION - Notification that Serial# is already set correctly 188 | #################################################################################################### 189 | function alreadySetMessage() { 190 | echo "Thunderbolt display serial already set correctly" 191 | echo "Display in JSS: $perrid" 192 | echo "Display attached: $serial" 193 | } 194 | 195 | #################################################################################################### 196 | # SCRIPT OPERATIONS - - REALLY!!! - DO NOT MODIFY BELOW THIS LINE 197 | #################################################################################################### 198 | 199 | getSerialNumber 200 | if [ "$serial" != "" ]; then 201 | getInterface 202 | if [ "$en0" != "" ]; then 203 | getJSSInformationen0 204 | if [ "$periid" = "" ]; then 205 | getNextPeripheral 206 | generatePostXMLFile 207 | xmlPost 208 | deleteXMLFile 209 | elif [ "$periid" != "$serial" ]; then 210 | generatePutXMLFile 211 | xmlPut 212 | deleteXMLFile 213 | echo 214 | else 215 | alreadySetMessage 216 | fi 217 | elif [ "$en1" != "" ]; then 218 | getJSSInformationen1 219 | if [ "$periid" = "" ]; then 220 | getNextPeripheral 221 | generatePostXMLFile 222 | xmlPost 223 | deleteXMLFile 224 | elif [ "$periid" != "$serial" ]; then 225 | generatePutXMLFile 226 | xmlPut 227 | deleteXMLFile 228 | else 229 | alreadySetMessage 230 | fi 231 | else 232 | echo "No Computer Record Found!" 233 | fi 234 | else 235 | echo "No Thunderbolt Display Connected! - ABORT" 236 | fi 237 | -------------------------------------------------------------------------------- /updateDeviceInventory.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | #################################################################################################### 4 | # 5 | # Copyright (c) 2015, JAMF Software, LLC. All rights reserved. 6 | # 7 | # Redistribution and use in source and binary forms, with or without 8 | # modification, are permitted provided that the following conditions are met: 9 | # * Redistributions of source code must retain the above copyright 10 | # notice, this list of conditions and the following disclaimer. 11 | # * Redistributions in binary form must reproduce the above copyright 12 | # notice, this list of conditions and the following disclaimer in the 13 | # documentation and/or other materials provided with the distribution. 14 | # * Neither the name of the JAMF Software, LLC nor the 15 | # names of its contributors may be used to endorse or promote products 16 | # derived from this software without specific prior written permission. 17 | # 18 | # THIS SOFTWARE IS PROVIDED BY JAMF SOFTWARE, LLC "AS IS" AND ANY 19 | # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | # DISCLAIMED. IN NO EVENT SHALL JAMF SOFTWARE, LLC BE LIABLE FOR ANY 22 | # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23 | # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 | # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 25 | # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 27 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | # 29 | #################################################################################################### 30 | # 31 | # SUPPORT FOR THIS PROGRAM 32 | # 33 | # This program is distributed "as is" by JAMF Software, LLC. 34 | # 35 | #################################################################################################### 36 | # 37 | # ABOUT THIS PROGRAM 38 | # 39 | # NAME 40 | # updateDeviceInventory.py -- Update Mobile Device Inventory 41 | # 42 | # SYNOPSIS 43 | # /usr/bin/python updateDeviceInventory.py 44 | # 45 | # DESCRIPTION 46 | # This script was designed to update all mobile device inventory in a JSS. 47 | # 48 | # For the script to function properly, users must be running the JSS version 7.31 or later and 49 | # the account provided must have API privileges to "READ" and "UPDATE" mobile devices in the JSS. 50 | # 51 | #################################################################################################### 52 | # 53 | # HISTORY 54 | # 55 | # Version: 1.2 56 | # 57 | # - Created by Nick Amundsen on June 23, 2011 58 | # - Updated by Bram Cohen on May 12, 2016 59 | # Added TLSv1 and new JSON Response on 9.6+ 60 | # 61 | ##################################################################################################### 62 | # 63 | # DEFINE VARIABLES & READ IN PARAMETERS 64 | # 65 | ##################################################################################################### 66 | # 67 | # HARDCODED VALUES SET HERE 68 | # 69 | jss_host = "127.0.0.1" #Example: 127.0.0.1 if run on server 70 | jss_port = 8443 71 | jss_path = "" #Example: "jss" for a JSS at https://www.company.com:8443/jss 72 | jss_username = "admin" 73 | jss_password = "jamf1234" 74 | 75 | ##DONT EDIT BELOW THIS LINE 76 | import json 77 | import httplib 78 | import base64 79 | import urllib2 80 | import ssl 81 | import socket 82 | 83 | ##Computer Object Definition 84 | class Device: 85 | id = -1 86 | 87 | ##Check variable 88 | def verifyVariable(name, value): 89 | if value == "": 90 | print "Error: Please specify a value for variable \"" + name + "\"" 91 | sys.exit(1) 92 | 93 | ## the main function. 94 | def main(): 95 | verifyVariable("jss_host",jss_host) 96 | verifyVariable("jss_port",jss_port) 97 | verifyVariable("jss_username",jss_username) 98 | verifyVariable("jss_password",jss_password) 99 | devices=grabDeviceIDs() 100 | updateDeviceInventory(devices) 101 | 102 | ##Grab and parse the mobile devices and return them in an array. 103 | def grabDeviceIDs(): 104 | devices=[]; 105 | ## parse the list 106 | for deviceListJSON in (getDeviceListFromJSS()["mobile_devices"]): 107 | d = Device() 108 | d.id = deviceListJSON.get("id") 109 | devices.append(d) 110 | print "Found " + str(len(devices)) + " devices." 111 | return devices 112 | 113 | class TLS1Connection(httplib.HTTPSConnection): 114 | def __init__(self, host, **kwargs): 115 | httplib.HTTPSConnection.__init__(self, host, **kwargs) 116 | 117 | def connect(self): 118 | sock = socket.create_connection((self.host, self.port), self.timeout, self.source_address) 119 | if getattr(self, '_tunnel_host', None): 120 | self.sock = sock 121 | self._tunnel() 122 | 123 | self.sock = ssl.wrap_socket(sock, self.key_file, self.cert_file, ssl_version=ssl.PROTOCOL_TLSv1) 124 | 125 | 126 | class TLS1Handler(urllib2.HTTPSHandler): 127 | def __init__(self): 128 | urllib2.HTTPSHandler.__init__(self) 129 | 130 | def https_open(self, req): 131 | return self.do_open(TLS1Connection, req) 132 | 133 | 134 | 135 | ##Create a header for the request 136 | def getAuthHeader(u,p): 137 | # Compute base64 representation of the authentication token. 138 | token = base64.b64encode('%s:%s' % (u,p)) 139 | return "Basic %s" % token 140 | 141 | ##Download a list of all mobile devices from the JSS API 142 | def getDeviceListFromJSS(): 143 | print "Getting device list from the JSS..." 144 | opener = urllib2.build_opener(TLS1Handler()) 145 | 146 | request = urllib2.Request("https://" + str(jss_host) + ":" + str(jss_port) + str(jss_path) + "/JSSResource/mobiledevices") 147 | request.add_header("Authorization", "Basic " + base64.b64encode('%s:%s' % (jss_username,jss_password))) 148 | request.add_header("Accept", "application/json") 149 | request.get_method = lambda: 'GET' 150 | 151 | try: 152 | data = opener.open(request) 153 | return json.load(data) 154 | except httplib.HTTPException as inst: 155 | print "Exception: %s" % inst 156 | sys.exit(1) 157 | except ValueError as inst: 158 | print "Exception decoding JSON: %s" % inst 159 | sys.exit(1) 160 | 161 | ##Submit the command to update a device's inventory to the JSS 162 | def updateDeviceInventory(devices): 163 | print "Updating Devices Inventory..." 164 | ##Parse through each device and submit the command to update inventory 165 | for index, device in enumerate(devices): 166 | percent = "%.2f" % (float(index) / float(len(devices)) * 100) 167 | print str(percent) + "% Complete -" 168 | submitDataToJSS(device) 169 | print "100.00% Complete" 170 | 171 | ##Update data for a single device 172 | def submitDataToJSS(Device): 173 | print "\tSubmitting command to update device id " + str(Device.id) + "..." 174 | try: 175 | url = "https://" + str(jss_host) + ":" + str(jss_port) + str(jss_path) + "/JSSResource/mobiledevices/id/" + str(Device.id) 176 | #Write out the XML string with new data to be submitted 177 | newDataString = "UpdateInventory" 178 | #print "Data Sent: " + newDataString 179 | opener = urllib2.build_opener(TLS1Handler()) 180 | request = urllib2.Request(url,newDataString) 181 | request.add_header("Authorization", getAuthHeader(jss_username,jss_password)) 182 | request.add_header('Content-Type', 'application/xml') 183 | request.get_method = lambda: 'PUT' 184 | opener.open(request) 185 | except httplib.HTTPException as inst: 186 | print "\tException: %s" % inst 187 | except ValueError as inst: 188 | print "\tException submitting PUT XML: %s" % inst 189 | except urllib2.HTTPError as inst: 190 | print "\tException submitting PUT XML: %s" % inst 191 | except: 192 | print "\tUnknown error submitting PUT XML." 193 | 194 | ## Code starts executing here. Just call main. 195 | main() 196 | -------------------------------------------------------------------------------- /updateDeviceInventoryBySmartGroup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | #################################################################################################### 4 | # 5 | # Copyright (c) 2015, JAMF Software, LLC. All rights reserved. 6 | # 7 | # Redistribution and use in source and binary forms, with or without 8 | # modification, are permitted provided that the following conditions are met: 9 | # * Redistributions of source code must retain the above copyright 10 | # notice, this list of conditions and the following disclaimer. 11 | # * Redistributions in binary form must reproduce the above copyright 12 | # notice, this list of conditions and the following disclaimer in the 13 | # documentation and/or other materials provided with the distribution. 14 | # * Neither the name of the JAMF Software, LLC nor the 15 | # names of its contributors may be used to endorse or promote products 16 | # derived from this software without specific prior written permission. 17 | # 18 | # THIS SOFTWARE IS PROVIDED BY JAMF SOFTWARE, LLC "AS IS" AND ANY 19 | # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | # DISCLAIMED. IN NO EVENT SHALL JAMF SOFTWARE, LLC BE LIABLE FOR ANY 22 | # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23 | # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 | # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 25 | # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 27 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | # 29 | #################################################################################################### 30 | # 31 | # SUPPORT FOR THIS PROGRAM 32 | # 33 | # This program is distributed "as is" by JAMF Software, LLC. 34 | # 35 | #################################################################################################### 36 | # 37 | # ABOUT THIS PROGRAM 38 | # 39 | # NAME 40 | # updateDeviceInventoryBySmartGroup.py -- Update Mobile Device Inventory By Smart Group Membership 41 | # 42 | # SYNOPSIS 43 | # /usr/bin/python updateDeviceInventoryBySmartGroup.py 44 | # 45 | # DESCRIPTION 46 | # This script was designed to update all mobile device inventory in a JSS Smart Group. 47 | # 48 | # For the script to function properly, users must be running the JSS version 7.31 or later and 49 | # the account provided must have API privileges to "READ" and "UPDATE" mobile devices in the JSS. 50 | # 51 | #################################################################################################### 52 | # 53 | # HISTORY 54 | # 55 | # Version: 1.0 56 | # 57 | # - Created by Nick Amundsen on June 23, 2011 58 | # - Updated by Bram Cohen on March 19, 2015 59 | # Added TLSv1 and new JSON Response on 9.6+ 60 | # 61 | # - Forked by Bram Cohen on April 27, 2015 62 | # - Chanaged to target groups instead of all devices 63 | # 64 | ##################################################################################################### 65 | # 66 | # DEFINE VARIABLES & READ IN PARAMETERS 67 | # 68 | ##################################################################################################### 69 | # 70 | # HARDCODED VALUES SET HERE 71 | # 72 | jss_host = "" #Example: "www.company.com" for a JSS at https://www.company.com:8443/jss 73 | jss_port = "" #Example: "8443" for a JSS at https://www.company.com:8443/jss 74 | jss_path = "" #Example: "jss" for a JSS at https://www.company.com:8443/jss 75 | jss_username = "" #Example: Admin 76 | jss_password = "" #Example: Password 77 | jss_smart_group_id= "" #Example: "1" 78 | 79 | ##DONT EDIT BELOW THIS LINE 80 | import sys 81 | import json 82 | import httplib 83 | import base64 84 | import urllib2 85 | import ssl 86 | import socket 87 | 88 | ##Computer Object Definition 89 | class Device: 90 | id = -1 91 | 92 | ##Check variable 93 | def verifyVariable(name, value): 94 | if value == "": 95 | print "Error: Please specify a value for variable \"" + name + "\"" 96 | sys.exit(1) 97 | 98 | ## the main function. 99 | def main(): 100 | verifyVariable("jss_host",jss_host) 101 | verifyVariable("jss_port",jss_port) 102 | verifyVariable("jss_username",jss_username) 103 | verifyVariable("jss_password",jss_password) 104 | devices=grabDeviceIDs() 105 | updateDeviceInventory(devices) 106 | 107 | ##Grab and parse the mobile devices and return them in an array. 108 | def grabDeviceIDs(): 109 | devices=[]; 110 | ## parse the list 111 | for deviceListJSON in (getDeviceListFromJSS()["mobile_device_group"]["mobile_devices"]): 112 | d = Device() 113 | d.id = deviceListJSON.get("id") 114 | devices.append(d) 115 | print "Found " + str(len(devices)) + " devices." 116 | return devices 117 | 118 | ##Create a header for the request 119 | def getAuthHeader(u,p): 120 | # Compute base64 representation of the authentication token. 121 | token = base64.b64encode('%s:%s' % (u,p)) 122 | return "Basic %s" % token 123 | 124 | ##Download a list of all mobile devices from the JSS API 125 | def getDeviceListFromJSS(): 126 | print "Getting device list from the JSS..." 127 | headers = {"Authorization":getAuthHeader(jss_username,jss_password),"Accept":"application/json"} 128 | try: 129 | conn = httplib.HTTPSConnection(jss_host,jss_port) 130 | sock = socket.create_connection((conn.host, conn.port), conn.timeout, conn.source_address) 131 | conn.sock = ssl.wrap_socket(sock, conn.key_file, conn.cert_file, ssl_version=ssl.PROTOCOL_TLSv1) 132 | conn.request("GET",jss_path + "/JSSResource/mobiledevicegroups/id/" + jss_smart_group_id,None,headers) 133 | data = conn.getresponse().read() 134 | conn.close() 135 | return json.loads(data) 136 | except httplib.HTTPException as inst: 137 | print "Exception: %s" % inst 138 | sys.exit(1) 139 | except ValueError as inst: 140 | print "Exception decoding JSON: %s" % inst 141 | sys.exit(1) 142 | 143 | ##Submit the command to update a device's inventory to the JSS 144 | def updateDeviceInventory(devices): 145 | print "Updating Devices Inventory..." 146 | ##Parse through each device and submit the command to update inventory 147 | for index, device in enumerate(devices): 148 | percent = "%.2f" % (float(index) / float(len(devices)) * 100) 149 | print str(percent) + "% Complete -" 150 | submitDataToJSS(device) 151 | print "100.00% Complete" 152 | 153 | ##Update data for a single device 154 | def submitDataToJSS(Device): 155 | print "\tSubmitting command to update device id " + str(Device.id) + "..." 156 | try: 157 | url = "https://" + str(jss_host) + ":" + str(jss_port) + str(jss_path) + "/JSSResource/mobiledevices/id/" + str(Device.id) 158 | #Write out the XML string with new data to be submitted 159 | newDataString = "UpdateInventory" 160 | #print "Data Sent: " + newDataString 161 | opener = urllib2.build_opener(urllib2.HTTPHandler) 162 | request = urllib2.Request(url,newDataString) 163 | request.add_header("Authorization", getAuthHeader(jss_username,jss_password)) 164 | request.add_header('Content-Type', 'application/xml') 165 | request.get_method = lambda: 'PUT' 166 | opener.open(request) 167 | except httplib.HTTPException as inst: 168 | print "\tException: %s" % inst 169 | except ValueError as inst: 170 | print "\tException submitting PUT XML: %s" % inst 171 | except urllib2.HTTPError as inst: 172 | print "\tException submitting PUT XML: %s" % inst 173 | except: 174 | print "\tUnknown error submitting PUT XML." 175 | 176 | ## Code starts executing here. Just call main. 177 | main() 178 | --------------------------------------------------------------------------------