A new function for your
~/.zshrc
to retrieve the managed Apple admin account password from Jamf Pro
Background
With the enhancements included in the recently released Jamf Pro 10.49.0, we decided to take a more serious look at Jamf Pro LAPS.
In doing so, we wanted an easy way for authorized administrators to view the managed Apple admin account password via Terminal.
Reference
The following should be considered the minimum required reference material before implementing this approach in production (and we’re still testing in our Stage lane):
- Technical Paper: Local Administrator Password Solution for Jamf Pro
- How to Securely Manage Local Admin Passwords with Jamf Pro and LAPS
- bz please
lapss
[l]ocal [a]dministrator [p]assword [s]olution in the [s]tage lane
Privileges
In limited testing, we granted the following privileges to our dedicated API LAPS Jamf Pro account:
- Jamf Pro Server Objects
- Computers
- Read
- Computers
- Jamf Pro Server Actions
- View Local Admin Password
- Enabled
- View Local Admin Password
Configuration
After adding the lapss
function to your ~/.zshrc
, quit and re-launch Terminal, then enter:
lapss
You should then observe the following:
### # [l]ocal [a]dministrator [p]assword [s]olution in the [s]tage lane ### Usage: 1. Type "lapss", followed by a [Space] 2. Paste the computer's Serial Number or Jamf Pro Computer ID 3. Press [Return] (Enter "iaps help" for initial setup.)
But, if you’re like me, you’d say to yourself …
You’re not the boss of me, script!
… and you’d enter the following straightaway:
lapss C86753099TT
Then — if you haven’t followed the help
instructions — you’d observe:
### # ERROR: Unable to read API credentials ### Please run "lapss help" to configure API credentials.
OK! I’ll enter lapss help
and follow the on-screen instructions:
### # [l]ocal [a]dministrator [p]assword [s]olution in the [s]tage lane Help # (some code inspired by: Joshua Smith) ### Use the following commands to create entries in Keychain Access: security add-generic-password -s "lapssApiUrl" -a dan -w "https://snelson.jamfcloud.com/" security add-generic-password -s "lapssApiUser" -a dan -w "API Username Goes Here" security add-generic-password -s "lapssApiPassword" -a dan -w "API Password Goes Here" Once the Keychain entries have been created … 1. Enter "lapss", followed by a [Space] 2. Paste the computer's Serial Number or Jamf Pro Computer ID 3. Press [Return]
Usage
Now, after the configuration in complete, when you enter lapss
followed by a Serial Number of Jamf Pro Computer ID, you should observe the following:
❯ lapss C86753099TT Found 1 nodes in stdin: -- NODE -- Retrieving 'localadmin' password for: • Name: Dan's MacBook Air M1 2020 • Serial Number: C86753099TT • IP Address: 10.10.128.45 • IP Address (LR): 192.168.40.162 Via MDM: • Server: https://snelson.jamfcloud.com/ • Computer ID: 157 localadmin password for S/N C86753099TT displayed below and copied to the clipboard: BWTYGJ-UUOOME-OWT553-KFKL4OHA
Function
lapss
lapss () { # [l]ocal [a]dministrator [p]assword [s]olution in the [s]tage lane expectedLocalAdminAccount="localadmin" if [ -z ${1} ]; then printf "\n###\n# [l]ocal [a]dministrator [p]assword [s]olution in the [s]tage lane\n###\n\n" printf "Usage:\n1. Type \"lapss\", followed by a [Space]\n2. Paste the computer's Serial Number or Jamf Pro Computer ID\n3. Press [Return]\n\n" printf "(Enter \"lapss help\" for initial setup.)\n\n" return fi if [ ${1} = "help" ]; then apiURL=$( /usr/bin/defaults read "/Library/Preferences/com.jamfsoftware.jamf.plist" jss_url ) /usr/bin/clear printf "\n###\n# [l]ocal [a]dministrator [p]assword [s]olution in the [s]tage lane Help\n# (some code inspired by: Joshua Smith)\n###\n\n" printf "Use the following commands to create entries in Keychain Access:\n\n" printf " security add-generic-password -s \"lapssApiUrl\" -a ${USER} -w \"${apiURL}\"\n" printf " security add-generic-password -s \"lapssApiUser\" -a ${USER} -w \"API Username Goes Here\"\n" printf " security add-generic-password -s \"lapssApiPassword\" -a ${USER} -w \"API Password Goes Here\"\n\n" printf "Once the Keychain entries have been created …\n\n" printf " 1. Enter \"lapss\", followed by a [Space]\n 2. Paste the computer's Serial Number or Jamf Pro Computer ID\n 3. Press [Return]\n\n" else apiURL=$( security find-generic-password -s "lapssApiUrl" -a "${USER}" -w 2>/dev/null ) apiUser=$( security find-generic-password -s "lapssApiUser" -a "${USER}" -w 2>/dev/null ) apiPassword=$( security find-generic-password -s "lapssApiPassword" -a "${USER}" -w 2>/dev/null ) if [ -z ${apiURL} ] || [ -z ${apiUser} ] || [ -z ${apiUser} ]; then printf "\n###\n# ERROR: Unable to read API credentials\n###\n\nPlease run \"lapss help\" to configure API credentials.\n\n" return fi # Obtain Jamf Pro Bearer Token via Basic Authentication apiBearerToken=$( /usr/bin/curl -X POST --silent -u "${apiUser}:${apiPassword}" "${apiURL}/api/v1/auth/token" | /usr/bin/plutil -extract token raw - ) # echo "apiBearerToken: ${apiBearerToken}" # Determine if user provided a Jamf Pro Computer ID (i.e., a number) or a Serial Number (i.e., NOT a number) numberValidation='^[0-9]+$' if ! [[ "${1}" =~ "${numberValidation}" ]] ; then # Determine the computer's Jamf Pro Computer ID via the computer's Serial Number, "${1}" jssID=$( /usr/bin/curl -H "Authorization: Bearer ${apiBearerToken}" -s "${apiURL}"/JSSResource/computers/serialnumber/"${1}"/subset/general | xpath -e "/computer/general/id/text()" ) else jssID="${1}" fi # echo "JSS ID: ${jssID}" generalComputerInfo=$( /usr/bin/curl -H "Authorization: Bearer ${apiBearerToken}" -H "Accept: text/xml" -sfk "${apiURL}"/JSSResource/computers/id/"${jssID}/subset/General" -X GET ) # echo "generalComputerInfo: ${generalComputerInfo}" computerName=$( echo ${generalComputerInfo} | xpath -q -e "/computer/general/name/text()" ) computerSerialNumber=$( echo ${generalComputerInfo} | xpath -q -e "/computer/general/serial_number/text()" ) computerIpAddress=$( echo ${generalComputerInfo} | xpath -q -e "/computer/general/ip_address/text()" ) computerIpAddressLastReported=$( echo ${generalComputerInfo} | xpath -q -e "/computer/general/last_reported_ip/text()" ) printf "\nRetrieving '${expectedLocalAdminAccount}' password for:\n" printf "• Name: $computerName\n" printf "• Serial Number: $computerSerialNumber\n" printf "• IP Address: $computerIpAddress\n" printf "• IP Address (LR): $computerIpAddressLastReported\n\n" printf "Via MDM:\n" printf "• Server: ${apiURL}\n" # printf "• Username: ${apiUser}\n" printf "• Computer ID: ${jssID}\n\n" computerInventoryGeneral=$( /usr/bin/curl -H "Authorization: Bearer ${apiBearerToken}" -s "${apiURL}"/api/v1/computers-inventory/"${jssID}"?section=GENERAL -H "accept: application/json" ) # on-prem # computerInventoryGeneral=$( /usr/bin/curl -H "Authorization: Bearer ${apiBearerToken}" -s "${apiURL}/api/v1/computers-inventory?section=GENERAL&filter=id==${jssID}" -H "accept: application/json" -X GET ) # thanks, @Alec V; Jamf Cloud ??? # echo "computerInventoryGeneral: ${computerInventoryGeneral}" managementId=$( echo "${computerInventoryGeneral}" | awk '/managementId/{print $NF}' | tr -d '",' ) # echo "managementId: ${managementId}" localAdminAccountsRaw=$( /usr/bin/curl -H "Authorization: Bearer ${apiBearerToken}" -s "${apiURL}"/api/v1/local-admin-password/${managementId}/accounts -H "accept: application/json" ) # echo "localAdminAccountsRaw: ${localAdminAccountsRaw}" username=$( echo "${localAdminAccountsRaw}" | awk '/username/{print $NF}' | tr -d '",' ) # echo "username: ${username}" if [[ "${username}" == *"${expectedLocalAdminAccount}"* ]]; then # echo "Found Expected Local Admin Account '${expectedLocalAdminAccount}'; proceeding …" localAdminPasswordRaw=$( /usr/bin/curl -H "Authorization: Bearer ${apiBearerToken}" -s "${apiURL}"/api/v1/local-admin-password/${managementId}/account/${expectedLocalAdminAccount}/password -H "accept: application/json" ) # echo "localAdminPasswordRaw: ${localAdminPasswordRaw}" password=$( echo "${localAdminPasswordRaw}" | awk '/password/{print $NF}' | tr -d '",' ) # echo "password: ${password}" printf "\n${expectedLocalAdminAccount} password for S/N ${1}\ndisplayed below and copied to the clipboard:\n\n${prefix}${password}\n\n" echo "${password}" | tr -d '\n' | pbcopy # Invalidate the Bearer Token apiBearerToken=$( /usr/bin/curl "${apiURL}/api/v1/auth/invalidate-token" --silent --header "Authorization: Bearer ${apiBearerToken}" -X POST ) apiBearerToken="" printf "\n\n" else echo "Expected Local Admin Account '${expectedLocalAdminAccount}' NOT found; exiting." return fi fi }