-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathJamf_Pro_Computer_Script_Download.sh
249 lines (181 loc) · 9.27 KB
/
Jamf_Pro_Computer_Script_Download.sh
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
#!/bin/bash
# This script is designed to use the Jamf Pro API to identify the individual IDs of
# the scripts stored on a Jamf Pro server then do the following:
#
# 1. Download the script as XML
# 2. Identify the script name
# 3. Extract the script contents from the downloaded XML
# 4. Save the script to a specified directory
# If setting up a specific user account with limited rights, here are the required API privileges
# for the account on the Jamf Pro server:
#
# Jamf Pro Server Objects:
#
# Scripts: Read
# https://derflounder.wordpress.com/2018/12/08/backing-up-macos-scripts-from-jamf-pro/
# If you choose to specify a directory to save the downloaded scripts into,
# please enter the complete directory path into the ScriptDownloadDirectory
# variable below.
ScriptDownloadDirectory="DIRECTORY"
# If the ScriptDownloadDirectory isn't specified above, a directory will be
# created and the complete directory path displayed by the script.
if [[ -z "$ScriptDownloadDirectory" ]]; then
ScriptDownloadDirectory=$(mktemp -d)
echo "A location to store downloaded scripts has not been specified."
echo "Downloaded scripts will be stored in $ScriptDownloadDirectory."
fi
# If you're on Jamf Pro 10.34.2 or earlier, which doesn't support using Bearer Tokens
# for Classic API authentication, set the NoBearerToken variable to the following value
# as shown below:
#
# yes
#
# NoBearerToken="yes"
#
# If you're on Jamf Pro 10.35.0 or later, which does support using Bearer Tokens
# for Classic API authentication, set the NoBearerToken variable to the following value
# as shown below:
#
# NoBearerToken=""
NoBearerToken=""
GetJamfProAPIToken() {
# This function uses Basic Authentication to get a new bearer token for API authentication.
# Use user account's username and password credentials with Basic Authorization to request a bearer token.
if [[ $(/usr/bin/sw_vers -productVersion | awk -F . '{print $1}') -lt 12 ]]; then
api_token=$(/usr/bin/curl -X POST --silent -u "${jamfpro_user}:${jamfpro_password}" "${jamfpro_url}/api/v1/auth/token" | python -c 'import sys, json; print json.load(sys.stdin)["token"]')
else
api_token=$(/usr/bin/curl -X POST --silent -u "${jamfpro_user}:${jamfpro_password}" "${jamfpro_url}/api/v1/auth/token" | plutil -extract token raw -)
fi
}
APITokenValidCheck() {
# Verify that API authentication is using a valid token by running an API command
# which displays the authorization details associated with the current API user.
# The API call will only return the HTTP status code.
api_authentication_check=$(/usr/bin/curl --write-out %{http_code} --silent --output /dev/null "${jamfpro_url}/api/v1/auth" --request GET --header "Authorization: Bearer ${api_token}")
}
CheckAndRenewAPIToken() {
# Verify that API authentication is using a valid token by running an API command
# which displays the authorization details associated with the current API user.
# The API call will only return the HTTP status code.
APITokenValidCheck
# If the api_authentication_check has a value of 200, that means that the current
# bearer token is valid and can be used to authenticate an API call.
if [[ ${api_authentication_check} == 200 ]]; then
# If the current bearer token is valid, it is used to connect to the keep-alive endpoint. This will
# trigger the issuing of a new bearer token and the invalidation of the previous one.
if [[ $(/usr/bin/sw_vers -productVersion | awk -F . '{print $1}') -lt 12 ]]; then
api_token=$(/usr/bin/curl "${jamfpro_url}/api/v1/auth/keep-alive" --silent --request POST --header "Authorization: Bearer ${api_token}" | python -c 'import sys, json; print json.load(sys.stdin)["token"]')
else
api_token=$(/usr/bin/curl "${jamfpro_url}/api/v1/auth/keep-alive" --silent --request POST --header "Authorization: Bearer ${api_token}" | plutil -extract token raw -)
fi
else
# If the current bearer token is not valid, this will trigger the issuing of a new bearer token
# using Basic Authentication.
GetJamfProAPIToken
fi
}
InvalidateToken() {
# Verify that API authentication is using a valid token by running an API command
# which displays the authorization details associated with the current API user.
# The API call will only return the HTTP status code.
APITokenValidCheck
# If the api_authentication_check has a value of 200, that means that the current
# bearer token is valid and can be used to authenticate an API call.
if [[ ${api_authentication_check} == 200 ]]; then
# If the current bearer token is valid, an API call is sent to invalidate the token.
authToken=$(/usr/bin/curl "${jamfpro_url}/api/v1/auth/invalidate-token" --silent --header "Authorization: Bearer ${api_token}" -X POST)
# Explicitly set value for the api_token variable to null.
api_token=""
fi
}
# If you choose to hardcode API information into the script, set one or more of the following values:
#
# The username for an account on the Jamf Pro server with sufficient API privileges
# The password for the account
# The Jamf Pro URL
# Set the Jamf Pro URL here if you want it hardcoded.
jamfpro_url="URL"
# Set the username here if you want it hardcoded.
jamfpro_user="USERNAME"
# Set the password here if you want it hardcoded.
jamfpro_password="PASSWORD"
# If you do not want to hardcode API information into the script, you can also store
# these values in a ~/Library/Preferences/com.github.jamfpro-info.plist file.
#
# To create the file and set the values, run the following commands and substitute
# your own values where appropriate:
#
# To store the Jamf Pro URL in the plist file:
# defaults write com.github.jamfpro-info jamfpro_url https://jamf.pro.server.goes.here:port_number_goes_here
#
# To store the account username in the plist file:
# defaults write com.github.jamfpro-info jamfpro_user account_username_goes_here
#
# To store the account password in the plist file:
# defaults write com.github.jamfpro-info jamfpro_password account_password_goes_here
#
# If the com.github.jamfpro-info.plist file is available, the script will read in the
# relevant information from the plist file.
if [[ -f "$HOME/Library/Preferences/com.github.jamfpro-info.plist" ]]; then
if [[ -z "$jamfpro_url" ]]; then
jamfpro_url=$(defaults read $HOME/Library/Preferences/com.github.jamfpro-info jamfpro_url)
fi
if [[ -z "$jamfpro_user" ]]; then
jamfpro_user=$(defaults read $HOME/Library/Preferences/com.github.jamfpro-info jamfpro_user)
fi
if [[ -z "$jamfpro_password" ]]; then
jamfpro_password=$(defaults read $HOME/Library/Preferences/com.github.jamfpro-info jamfpro_password)
fi
fi
# If the Jamf Pro URL, the account username or the account password aren't available
# otherwise, you will be prompted to enter the requested URL or account credentials.
if [[ -z "$jamfpro_url" ]]; then
read -p "Please enter your Jamf Pro server URL : " jamfpro_url
fi
if [[ -z "$jamfpro_user" ]]; then
read -p "Please enter your Jamf Pro user account : " jamfpro_user
fi
if [[ -z "$jamfpro_password" ]]; then
read -p "Please enter the password for the $jamfpro_user account: " -s jamfpro_password
fi
echo ""
# Remove the trailing slash from the Jamf Pro URL if needed.
jamfpro_url=${jamfpro_url%%/}
# If configured to get one, get a Jamf Pro API Bearer Token
if [[ -z "$NoBearerToken" ]]; then
GetJamfProAPIToken
fi
# Remove the trailing slash from the ScriptDownloadDirectory variable if needed.
ScriptDownloadDirectory=${ScriptDownloadDirectory%%/}
DownloadScript(){
# Download the script information as raw XML,
# then format it to be readable.
echo "Downloading scripts from $jamfpro_url..."
if [[ -z "$NoBearerToken" ]]; then
CheckAndRenewAPIToken
FormattedScript=$(/usr/bin/curl -s --header "Authorization: Bearer ${api_token}" -H "Accept: application/xml" "${jamfpro_url}/JSSResource/scripts/id/${ID}" -X GET | xmllint --format - )
else
FormattedScript=$(/usr/bin/curl -su "${jamfpro_user}:${jamfpro_password}" -H "Accept: application/xml" "${jamfpro_url}/JSSResource/scripts/id/${ID}" -X GET | xmllint --format - )
fi
# Identify and display the script's name.
DisplayName=$(echo "$FormattedScript" | xmllint --xpath "/script/name/text()" - 2>/dev/null | sed -e 's|:|(colon)|g' -e 's/\//\\/g')
echo "Downloaded script is named: $DisplayName"
## Save the downloaded script
echo "Saving ${DisplayName} file to $ScriptDownloadDirectory."
echo "$FormattedScript" | xmllint --xpath '/script/script_contents/text()' - 2>/dev/null | sed -e 's/</</g' -e 's/>/>/g' -e 's/"/"/g' -e 's/&/\&/g' > "$ScriptDownloadDirectory/${DisplayName}"
}
if [[ -z "$NoBearerToken" ]]; then
CheckAndRenewAPIToken
Script_id_list=$(/usr/bin/curl -s --header "Authorization: Bearer ${api_token}" -H "Accept: application/xml" "${jamfpro_url}/JSSResource/scripts" | xmllint --xpath "//id" - 2>/dev/null)
else
Script_id_list=$(/usr/bin/curl -su "${jamfpro_user}:${jamfpro_password}" -H "Accept: application/xml" "${jamfpro_url}/JSSResource/scripts" | xmllint --xpath "//id" - 2>/dev/null)
fi
Script_id=$(echo "$Script_id_list" | grep -Eo "[0-9]+")
# Download latest version of all advanced computer searches. For performance reasons, we
# parallelize the execution.
MaximumConcurrentJobs=10
ActiveJobs=0
for ID in ${Script_id}; do
((ActiveJobs=ActiveJobs%MaximumConcurrentJobs)); ((ActiveJobs++==0)) && wait
DownloadScript &
done