Menu Close

bz please

Functions for your ~/.zshrc to hopefully make your life as a Mac Admin easier

Background

After reading Armin Briegel’s macOS Terminal and Shell some time ago, I started adding a handful of functions to my ~/.zshrc file to help automate various day-to-day tasks.

The first function started from a trick Brock Walters taught us at a Jamf Certification class:

# Backup files by Brock Walters
# Create a timestamped backup of "source" in the same directory
cp -v source{,-backup-`date '+%Y-%m-%d-%H%M%S'`}

We’ll use this command as part of our first ~/.zshrc function.

bz

Before editing ~/.zshrc, we want to ensure we have a known-working backup for when things go sideways.

Launch the macOS Terminal and enter the following command:

cp -v ~/.zshrc{,-backup-`date '+%Y-%m-%d-%H%M%S'`}

The output should look similar to the following:

/Users/dan/.zshrc -> /Users/dan/.zshrc-backup-2022-05-26-185628

Now that we have a known-working backup of ~/.zshrc (i.e., ~/.zshrc-backup-2022-05-26-185628), we can start adding functions.

The first function will create a timestamped backup ~/.zshrc and then open it in Visual Studio Code for editing.

Open ~/.zshrc in your preferred editor, add the following and save:

###
# Functions
###

bz () {     # [b]ackup ~/.[z]shrc: Backup ~/.zshrc
    if [ -z ${1} ]; then
        printf "\n###\n# [b]ackup ~/.[z]shrc: Backup ~/.zshrc\n###\n\n"
        printf "Usage:\n1. Type \"bz\", followed by a [Space]\n2. Type \"please\"\n3. Press [Return]\n\n"
        return
    fi

    cp -v ~/.zshrc{,-backup-`date '+%Y-%m-%d-%H%M%S'`}

    printf "\nEditing ~/.zshrc in Visual Studio Code …\n\n"
    code ~/.zshrc

}

Quit and re-launch Terminal and enter bz. You should observe the following in Terminal:

###
# [b]ackup ~/.[z]shrc: Backup ~/.zshrc
###

Usage:
1. Type "bz", followed by a [Space]
2. Type "please"
3. Press [Return]

Next, type bz please and you should observe the following in Terminal …

bz please
/Users/dan/.zshrc -> /Users/dan/.zshrc-backup-2022-05-26-192704

Editing ~/.zshrc in Visual Studio Code …

… followed by ~/.zshrc being opened for editing in Visual Studio Code.

Actually, you don’t have to enter the word please after bz, anything will do; some days when I’m not feeling as charitable as I should, I enter: bz now!

rq

The next function removes quarantine extended attributes from any file which you drag-and-drop into the open Terminal window.

rq () {     # [r]emove [q]uarantine: Remove quarantine extended attributes from downloads
    if [ -z ${1} ]; then
        printf "\n###\n# [r]emove [q]uarantine: Remove quarantine extended attributes from downloads\n###\n\n"
        printf "Usage:\n1. Type \"rq\", followed by a [Space]\n2. Drag-and-drop the downloaded file\n3. Press [Return]\n4. Enter administrative credentials\n\n"
        return
    fi

    downloadName=$( echo ${1} | awk -F '/' '{print $NF}' )

    printf "\n###\n# Current attributes of \"${downloadName}\" ...\n###\n\n"
    ls -lah@  "${1}"

    printf "\n\n###\n# Enter administrative credentials to remove quarantine extended attributes from \"${downloadName}\" ...\n###\n\n"
    /usr/bin/sudo /usr/bin/xattr -crv "${1}"

    printf "\n###\n# New attributes of \"${downloadName}\" ...\n###\n\n"
    ls -lah@  "${1}"

}

Entering rq in Terminal should result in the following:

###
# [r]emove [q]uarantine: Remove quarantine extended attributes from downloads
###

Usage:
1. Type "rq", followed by a [Space]
2. Drag-and-drop the downloaded file
3. Press [Return]
4. Enter administrative credentials

The next time you need to un-quarantine a file, type rq followed by a [space] and then drag-and-drop the file to the open Terminal window and press Return.

You’ll first see a listing of the file’s current extended attributes and you’ll be prompted for administrative credentials after-which the extended attributes will be removed and you’ll see a list of the file’s new extended attributes.

Reminder: After you edit ~/.zshrc, be sure to quit and re-launch Terminal to load the latest version.

help

Once I had more than a few functions, I needed an easy way for other Mac Admins to remember which command did what. Thus, the help function was born (which I place as the topmost function).

help () {   # help
    printf "\n\n###\n# [help]\n###\n\n"
    cat ~/.zshrc | grep '() {' | tr -d '(){'
    printf "\n\n"

}

rlp

If you leverage Recovery Lock in Jamf Pro, the following function will help you provide the password in a more human-readable format:

rlp () {     # [r]ecovery [l]ock [p]assword: Recovery Lock Password
    if [ -z ${1} ]; then
        printf "\n###\n# [r]ecovery [l]ock [p]assword: Recovery Lock Password\n###\n\n"
        printf "Usage:\n1. Type \"rlp\", followed by a [Space]\n2. Paste the computer's Recovery Lock Password\n3. Press [Return]\n\n"
        return
    fi

    recoveryLockPasswordHumanReadable=$( echo ${1} | sed 's/.\{4\}/& /g' )
    printf "\nA human-readable Recovery Lock Password is displayed below and has been copied to the clipboard:\n${recoveryLockPasswordHumanReadable}\n\n"
    echo "${recoveryLockPasswordHumanReadable}" | tr -d '\n' | pbcopy

}

As an example:

❯ rlp 987654321098765432109876

A human-readable Recovery Lock Password is displayed below and has been copied to the clipboard:
9876 5432 1098 7654 3210 9876 

eardm

With recent changes in macOS Monterey, Apple Remote Desktop needs to be enabled via MDM.

The following quick-and-dirty function — once fully configured — will allow you to enter eardm followed by the Jamf Pro Computer ID to enable ARD via the Classic API.

eardm () {    # [e]nable [a]pple [r]emote [d]esktop via [m]dm
    if [ -z ${1} ]; then
        printf "\n###\n# [e]nable [a]pple [r]emote [d]esktop via [m]dm\n###\n\n"
        printf "Usage:\n1. Type \"eardm\", followed by a [Space]\n2. Paste the computer's Jamf Pro Computer ID\n3. Press [Return]\n\n"
        printf "(Enter \"eardm help\" for initial setup.)\n\n"
        return
    fi

    if [ ${1} = "help" ]; then

        /usr/bin/clear
        printf "\n###\n# [e]nable [a]pple [r]emote [d]esktop via [m]dm Help\n###\n\n"
        printf "Use the following commands to create entries in Keychain Access:\n\n"
        printf "    security add-generic-password -s \"eardmApiUrl\" -a ${USER} -w \"API URL Goes Here\"\n"
        printf "    security add-generic-password -s \"eardmApiUser\" -a ${USER} -w \"API Username Goes Here\"\n"
        printf "    security add-generic-password -s \"eardmApiPassword\" -a ${USER} -w \"API Password Goes Here\"\n\n"
        printf "Once the Keychain entries have been created …\n\n"
        printf "    1. Enter \"eardm\", followed by a [Space]\n    2. Paste the computer's Jamf Pro Computer ID\n    3. Press [Return]\n\n"

    else

        apiURL=$( security find-generic-password -s "eardmApiUrl" -a "${USER}" -w 2>/dev/null )
        apiUser=$( security find-generic-password -s "eardmApiUser" -a "${USER}" -w 2>/dev/null )
        apiPassword=$( security find-generic-password -s "eardmApiPassword" -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 \"eardm help\" to configure API credentials.\n\n"
            return
        fi

        jssID=${1}
        generalComputerInfo=$( curl -H "Accept: text/xml" -sfku "${apiUser}:${apiPassword}" "${apiURL}"/JSSResource/computers/id/"${jssID}/subset/General" -X GET )

        # echo ${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 "\nEnabling Apple Remote Desktop 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"

        curl -H "Accept: text/xml" --progress-bar --fail-with-body --user "${apiUser}:${apiPassword}" "${apiURL}"/JSSResource/computercommands/command/EnableRemoteDesktop/id/"${jssID}" -X POST

        printf "\n\n"

    fi
}

To get started, enter eardm help and follow the one-time configuration instructions.

You’ll need an account in Jamf Pro with (at least) the following privileges:

  • Jamf Pro Server Objects
    • Computers: Create
    • Computers: Read
  • Jamf Pro Server Actions
    • Send Computer Remote Desktop Command

While I’ve tried — in vain — to use the Jamf Pro GUI to re-enable ARD on a Monterey box which was working before I rebooted it, this function has yet to fail.

ARD is already enabled and NOT working!

Aliases

Finally, it’s always nice to include aliases in your ~/.zshrc

###
# Command Aliases
###

alias ll="/bin/ls -lah"



###
# Path Aliases
###

alias mrs="cd ~/Documents/MunkiReport/munkireport-php-5.7.1-stage ; pwd"
alias mrp="cd ~/Documents/MunkiReport/munkireport-php-5.7.1-prod ; pwd"
alias scripts="cd ~/Documents/Scripts/ ; pwd"

Update (27-May-2022)

Here’s an update to eardm the way it should have been in the first place.

eardm2

Version 2 of the function to enable Apple Remote Desktop via Jamf Pro’s Classic API now takes a computer’s Jamf Pro Computer ID or Serial Number — something you’re more likely to have — as input and leverages Bearer Token Authentication.

eardm2 () { # [e]nable [a]pple [r]emote [d]esktop via [m]dm, version [2]
    if [ -z ${1} ]; then
        printf "\n###\n# [e]nable [a]pple [r]emote [d]esktop via [m]dm, version [2]\n###\n\n"
        printf "Usage:\n1. Type \"eardm2\", followed by a [Space]\n2. Paste the computer's Serial Number or Jamf Pro Computer ID\n3. Press [Return]\n\n"
        printf "(Enter \"eardm2 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# [e]nable [a]pple [r]emote [d]esktop via [m]dm, version [2] Help\n###\n\n"
        printf "Use the following commands to create entries in Keychain Access:\n\n"
        printf "    security add-generic-password -s \"eardm2ApiUrl\" -a ${USER} -w \"${apiURL}\"\n"
        printf "    security add-generic-password -s \"eardm2ApiUser\" -a ${USER} -w \"API Username Goes Here\"\n"
        printf "    security add-generic-password -s \"eardm2ApiPassword\" -a ${USER} -w \"API Password Goes Here\"\n\n"
        printf "Once the Keychain entries have been created …\n\n"
        printf "    1. Enter \"eardm2\", 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 "eardm2ApiUrl" -a "${USER}" -w 2>/dev/null )
        apiUser=$( security find-generic-password -s "eardm2ApiUser" -a "${USER}" -w 2>/dev/null )
        apiPassword=$( security find-generic-password -s "eardm2ApiPassword" -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 \"eardm2 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 - )

        # 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

        generalComputerInfo=$( /usr/bin/curl -H "Authorization: Bearer ${apiBearerToken}" -H "Accept: text/xml" -sfk "${apiURL}"/JSSResource/computers/id/"${jssID}/subset/General" -X GET )

        # echo ${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 "\nEnabling Apple Remote Desktop 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"

        /usr/bin/curl -H "Authorization: Bearer ${apiBearerToken}" -H "Accept: text/xml" --progress-bar --fail-with-body "${apiURL}"/JSSResource/computercommands/command/EnableRemoteDesktop/id/"${jssID}" -X POST

        # 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"

    fi
}

Update (02-Jun-2022)

oij

If you happen to run a clustered Jamf Pro server, the following may prove helpful to open computer record URLs included in automated Jamf Pro email alerts.

When an automated Jamf Pro email alert includes a link to a computer record, it references the specified Jamf Pro URL. (Not sure what else it could reference.)

However, if you purposely have Limited Access enabled for those nodes, you’re unable to view the computer record.

When adding the oij function to your ~/.zshrc, edit the jssURL to your internal server.

The computerRecordTab variable specifies which tab is initially selected:

  • Inventory: &o=r
  • Management: &o=r&v=management
  • History: &o=r&v=history

Since, by default, zsh interprets the computer record’s URL query string (?) as a pattern to be matched, it’s probably easiest just to enclose the URL in quotes.

(My personal preference is to add unsetopt nomatch to ~/.zshrc, type oij and then drag-and-drop the URL to the open Terminal window and press Return.)

oij () {    # [o]pen [i]n [j]amf pro
    # Manually specify your Jamf Pro administrative URL to a computer record
    jssURL="https://on.prem.jamfpro.company.com/computers.html?id="
    computerRecordTab="&o=r&v=history"
    if [ -z ${1} ]; then
        printf "\n###\n# [o]pen [i]n [j]amf pro\n###\n\n"
        printf "Usage:\n1. Type \"oij\", followed by a [Space] and an open quote: \"\n2. Paste the Primary Node computer record URL, followed by a closing quote: \"\n3. Press [Return]\n\n"
        return
    fi

    id=$( echo -e "${1}" | cut -d "=" -f2  )
    
    printf "Opening $jssURL$id$computerRecordTab …\n\n"

    open $jssURL$id$computerRecordTab

}
Posted in API, Device Management, Jamf Pro, Scripts, Tips & Tricks

Related Posts