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
afterbz
, 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.
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 }