├── .github └── FUNDING.yml ├── images ├── Screen Shot 2017-11-09 at 8.26.16 PM.png ├── Screen Shot 2018-06-30 at 5.07.54 PM.png ├── Screen Shot 2018-06-30 at 6.06.30 PM.png ├── Screen Shot 2018-06-29 at 10.00.27 PM.png ├── Screen Shot 2018-06-29 at 10.03.06 PM.png └── Screen Shot 2021-01-16 at 10.11.01 AM.png ├── LICENSE ├── README.md └── Extension Attribute Update.sh /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | custom: ['https://snelson.us/sponsoring/'] 4 | -------------------------------------------------------------------------------- /images/Screen Shot 2017-11-09 at 8.26.16 PM.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dan-snelson/Internal-Beta-Test-Program/HEAD/images/Screen Shot 2017-11-09 at 8.26.16 PM.png -------------------------------------------------------------------------------- /images/Screen Shot 2018-06-30 at 5.07.54 PM.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dan-snelson/Internal-Beta-Test-Program/HEAD/images/Screen Shot 2018-06-30 at 5.07.54 PM.png -------------------------------------------------------------------------------- /images/Screen Shot 2018-06-30 at 6.06.30 PM.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dan-snelson/Internal-Beta-Test-Program/HEAD/images/Screen Shot 2018-06-30 at 6.06.30 PM.png -------------------------------------------------------------------------------- /images/Screen Shot 2018-06-29 at 10.00.27 PM.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dan-snelson/Internal-Beta-Test-Program/HEAD/images/Screen Shot 2018-06-29 at 10.00.27 PM.png -------------------------------------------------------------------------------- /images/Screen Shot 2018-06-29 at 10.03.06 PM.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dan-snelson/Internal-Beta-Test-Program/HEAD/images/Screen Shot 2018-06-29 at 10.03.06 PM.png -------------------------------------------------------------------------------- /images/Screen Shot 2021-01-16 at 10.11.01 AM.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dan-snelson/Internal-Beta-Test-Program/HEAD/images/Screen Shot 2021-01-16 at 10.11.01 AM.png -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Dan K. Snelson 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Your Internal Beta Test Program: Opt-in / Opt-out via Self Service 2 | 3 | ## Presentation 4 | 5 | - [JNUC 2019](https://www.youtube.com/watch?v=AhYPVvO7LwM) 6 | 7 | --- 8 | 9 | ## Scripts 10 | - [Extension Attribute Update.sh](Extension%20Attribute%20Update.sh) 11 | 12 | 13 | ![Screenshot of Self Service policy](images/Screen%20Shot%202018-06-29%20at%2010.00.27%20PM.png) 14 | 15 | --- 16 | 17 | ## Background 18 | 19 | Inspired by @elliotjordan's plea to obtain user feedback, we've been using a pop-up menu Computer Extension Attribute called "Testing Level" which has three options: 20 | - Alpha (i.e., bleeding-edge test machines) 21 | - Beta (i.e., direct team members) 22 | - Gamma (i.e., opt-in testers from various teams) 23 | 24 | ![Screenshot of Testing Level Extenstion Attribute](images/Screen%20Shot%202018-06-29%20at%2010.03.06%20PM.png) 25 | 26 | We then have Smart Computer Groups for each of the three levels and a fourth for "none" so we can more easily scope policies. 27 | - Testing: Alpha Group 28 | - Testing: Beta Group 29 | - Testing: Gamma Group 30 | - Testing: None 31 | 32 | ![Screenshot of Testing: None Smart Group](images/Screen%20Shot%202018-06-30%20at%205.07.54%20PM.png) 33 | 34 | This has been working well, but has required a Jamf Pro administrator to manually edit each computer record and specify the desired Testing Level. 35 | 36 | After being challenged by @mike.paul and @kenglish to leverage the API, a search revealed @seansb's [Updating Pop-Up Extension Attribute Value via API](https://www.jamf.com/jamf-nation/discussions/18307/) post and @mm2270's reply about [Results of single extension attribute via API](https://www.jamf.com/jamf-nation/discussions/15258/results-of-single-extension-attribute-via-api#responseChild93856) we had exactly what we needed. 37 | 38 | --- 39 | 40 | ## API Permissions for Computer Extension Attributes 41 | 42 | In my rather frustated testing, the API read / write account needs (at least) the following Jamf Pro Objects "Read" and "Update" permissions: 43 | 44 | - Computer Extension Attributes 45 | - Computers 46 | - User Extension Attributes 47 | - Users 48 | 49 | --- 50 | 51 | ## Script: Extension Attribute Update 52 | 53 | You'll need the [Client-side Functions](https://github.com/dan-snelson/Jamf-Pro-Scripts/tree/master/Client-side%20Functions) installed on each Mac and you'll need to update the "apiURL" in the [Extension Attribute Update.bash](https://github.com/dan-snelson/Jamf-Pro-Scripts/blob/master/Extension%20Attribute%20Update.bash) script which leverages parameters 4 though 7 for: 54 | 55 | - API Username 56 | - API Password 57 | - EA Name (i.e., "Testing Level") 58 | - EA Value (i.e., "Gamma" or "None") 59 | 60 | ![Screenshot of Extension Attribute Update.bash](images/Screen%20Shot%202018-06-30%20at%206.06.30%20PM.png) 61 | 62 | --- 63 | 64 | ## Opt-in Beta Test Program Self Service Policy 65 | 66 | Create an ongoing Self Sevice policy, scoped to "Testing: None" which includes a single Scripts option of "Update Extension Attribute" and specify: 67 | - API Username (Read / Write) 68 | - API Password (Read / Write) 69 | - EA Name (i.e., "Testing Level") 70 | - EA Value (i.e., "Gamma" or "None") 71 | 72 | --- 73 | 74 | ## Opt-out Beta Test Program Self Service Policy 75 | 76 | Clone your Opt-in policy and change EA Value to "None" to unset a computer's Testing Level; scope to your testing groups. 77 | 78 | --- 79 | 80 | ## Refresh Self Service when users Opt-in / Opt-out of your Internal Beta Test Program 81 | - [See blog](https://snelson.us/2021/01/refresh-self-service-when-users-opt-in-opt-out-of-your-internal-beta-test-program/#pppc) 82 | -------------------------------------------------------------------------------- /Extension Attribute Update.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | #################################################################################################### 3 | # 4 | # ABOUT 5 | # 6 | # Set a computer's Extension Attribute via the API 7 | # 8 | #################################################################################################### 9 | # 10 | # HISTORY 11 | # 12 | # Version 1.0, 30-Jul-2016, Dan K. Snelson 13 | # Original 14 | # Version 1.1, 17-Oct-2016, Dan K. Snelson 15 | # Updated to leverage an encyrpted API password 16 | # 17 | #################################################################################################### 18 | 19 | 20 | 21 | # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # 22 | # 23 | # Functions 24 | # 25 | # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # 26 | 27 | decryptPassword() { 28 | /bin/echo "${1}" | /usr/bin/openssl enc -aes256 -d -a -A -S "${2}" -k "${3}" 29 | } 30 | 31 | 32 | 33 | # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # 34 | # 35 | # Variables 36 | # 37 | # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # 38 | 39 | apiURL="https://jamfpro.company.com/" # JSS API URL with trailing forward slash 40 | apiUsername="${4}" # API Username 41 | apiPasswordEncrypted="${5}" # API Encrypted Password 42 | eaName="${6}" # Name of Extension Attribute (i.e., "Testing Level") 43 | eaValue="${7}" # Value for Extension Attribute (i.e., "Gamma" or "None") 44 | Salt="1234567890" # Salt (generated from Encrypt Password) 45 | Passphrase="abcdefghijklmnopqrstuvwxyz" # Passphrase (generated from Encrypt Password) 46 | computerUDID=$(/usr/sbin/system_profiler SPHardwareDataType | /usr/bin/awk '/Hardware UUID:/ { print $3 }') 47 | 48 | 49 | 50 | # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # 51 | # 52 | # Program 53 | # 54 | # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # 55 | 56 | echo "Set a computer's Extension Attribute via the API" 57 | 58 | echo "• Validate a value has been specified for all parameters ..." 59 | 60 | if [ ! -z "${apiUsername}" ] && [ ! -z "${apiPasswordEncrypted}" ] && [ ! -z "${eaName}" ] && [ ! -z "${eaValue}" ]; then 61 | # All script parameters have been specified, proceeding ... 62 | echo "• All script parameters have been specified, proceeding ..." 63 | apiPassword=$(decryptPassword ${apiPasswordEncrypted} ${Salt} ${Passphrase}) 64 | echo "• Extension Attribute Name: ${eaName}" 65 | echo "• Extension Attribute New Value: ${eaValue}" 66 | 67 | if [ ${eaValue} = "None" ]; then 68 | echo "• Extension Attribute Value is 'None'; remove value ${eaName}" 69 | eaValue="" 70 | fi 71 | 72 | # Read current value ... 73 | apiRead=`curl -H "Accept: text/xml" -sfu ${apiUsername}:${apiPassword} ${apiURL}/JSSResource/computers/udid/${computerUDID}/subset/extension_attributes | xmllint --format - | grep -A3 "${eaName}" | awk -F'>|<' '/value/{print $3}'` 74 | 75 | echo "• Extension Attribute ${eaName}'s Current Value: ${apiRead}" 76 | 77 | # Construct the API data ... 78 | apiData="${eaName}${eaValue}" 79 | 80 | apiPost=`curl -H "Content-Type: text/xml" -sfu ${apiUsername}:${apiPassword} ${apiURL}/JSSResource/computers/udid/${computerUDID} -d "${apiData}" -X PUT` 81 | 82 | /bin/echo ${apiPost} 83 | 84 | # Read the new value ... 85 | apiRead=`curl -H "Accept: text/xml" -sfu ${apiUsername}:${apiPassword} ${apiURL}/JSSResource/computers/udid/${computerUDID}/subset/extension_attributes | xmllint --format - | grep -A3 "${eaName}" | awk -F'>|<' '/value/{print $3}'` 86 | 87 | echo "• Extension Attribute ${eaName}'s New Value: ${apiRead}" 88 | 89 | else 90 | 91 | echo "Error: Parameters 4, 5, 6 and 7 not populated; exiting." 92 | exit 1 93 | 94 | fi 95 | 96 | 97 | 98 | exit 0 99 | --------------------------------------------------------------------------------