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