Menu Close

Jamf Pro LAPS: Retrieving Password via Terminal

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):

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
  • Jamf Pro Server Actions
    • View Local Admin Password
      • Enabled

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

}

Related Posts

Posted in API, Jamf Pro, Scripts, Tips & Tricks

Related Posts