├── 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 |
--------------------------------------------------------------------------------