Home > Jamf Pro, macOS, macOS Recovery, Scripting > Booting to macOS Recovery or Diagnostics via Jamf Pro’s Self Service

Booting to macOS Recovery or Diagnostics via Jamf Pro’s Self Service

One of the advantages provided by Jamf Pro’s Self Service is that you can use it to provide easy access to tools for your users or helpdesk folks. One such tool could be a script which helps folks boot to their Macs to one of the following Apple support services:

For more details, please see below the jump.

This script sets one of four boot arguments and passes it to NVRAM:

  • RecoveryModeDisk – Boots to the Recovery volume on your local boot drive
  • RecoveryModeNetwork – Boots to Internet Recovery
  • DiagsModeDisk – Boots to the Diagnostics or Apple Hardware Test volume on your local boot drive.
  • DiagsModeNetwork – Boots to Internet Diagnostics or Apple Hardware Test

Note: If booting to macOS Recovery, this script will set the logged-in account to have admin privileges. This is because, on Macs equipped with T2 security chips, an admin account is needed to be able to access the macOS Utilities tools in the Recovery environment.

Macos catalina recovery mode auth installer password

To set it up for use with Jamf Pro, do the following:

1. Add the script to Jamf Pro.

Screen Shot 2020 03 24 at 4 58 31 PM

Screen Shot 2020 03 24 at 4 58 35 PM

2. Create a new Self Service policy.

Screen Shot 2020 03 28 at 4 38 19 PM

Screen Shot 2020 03 24 at 5 02 19 PM

3. Select the script as part of the policy.
4. Set the policy’s Execution Frequency to Ongoing.

Screen Shot 2020 03 28 at 4 38 20 PM

5. Set target scope as desired.

Screen Shot 2020 03 24 at 5 03 17 PM

Note: While this script was written with Jamf Pro’s Self Service in mind, it should be adaptable with either no alteration or minor edits to other Self Service tools which can run scripts.

Once the policy is set up in Jamf Pro, it should look like this when run.

Screen Shot 2020 03 24 at 5 11 55 PM

Screen Shot 2020 03 24 at 5 12 03 PM

Screen Shot 2020 03 24 at 5 12 10 PM

When the Mac restarts, it should boot to Diagnostics or macOS Recovery (depending on your choices.)

Diagnostics checking mac

Screen Shot 2020 03 24 at 4 34 21 PM

The script is available below. It is also available from the following location on GitHub:

https://github.com/rtrouton/rtrouton_scripts/tree/master/rtrouton_scripts/set_recovery_or_diagnostics_boot_mode

#!/bin/bash
# Enables the Mac to boot into the following:
#
# * Recovery
# * Internet Recovery
# * Diagnostics
# * Internet Diagnostics
#
# For information about Diagnostics: https://support.apple.com/HT202731
# For information about Recovery: https://support.apple.com/HT201314
# some variables we have to declare first
exitCode=0
# This is our logging function. It logs to syslog and also prints the
# log message to STDOUT to make sure, it appears in the Jamf Pro policy log.
log()
{
local errorMessage="$1"
echo "$errorMessage"
/usr/bin/logger "SetBootMode: $errorMessage"
}
# Here we have a function that allows us to display a message box
# to the user. If Jamf Self Service is installed on the machine, we
# use its icon, otherwise we check for the existence of the system's
# AlertNoteIcon and use this, if available. If none of those icons
# are available, we use a generic one.
displayDialog()
{
local dialogMessage="$1"
local dialogButtons="$2"
local defaultButton="$3"
local dialogIcon="$4"
local dialogGiveUp="$5"
# set up our buttons and make sure we have at least
# an OK button if nothing else has been specified
if [[ -z "$dialogButtons" ]]; then
dialogButtons="\"OK\""
else
dialogButtons=$(echo "$dialogButtons" | /usr/bin/sed -e 's/^/"/' -e 's/$/"/' -e 's/,/","/g')
fi
# if not otherwise specified, the last button is enabled
local allButtons=$(echo "$dialogButtons" | /usr/bin/awk -F"," '{print NF-1}')
local lastButton=$(($allButtons + 1))
if [[ ! "$defaultButton" =~ ^[0-9]+$ || $defaultButton -le 0 || $defaultButton -gt $lastButton ]]; then defaultButton="$lastButton"; fi
# we set the dialog timeout to 0 if not otherwise specified
if [[ ! "$dialogGiveUp" =~ ^[0-9]+$ ]]; then dialogGiveUp=0; fi
# make sure double quotation marks are properly escaped
dialogMessage=$(echo "$dialogMessage" | /usr/bin/sed -e 's/"/\\\"/g')
# get the currently logged-in user
currentUser=$(/bin/ls -l /dev/console | /usr/bin/awk '{ print $3 }')
# set the dialog's icon
if [[ ! "$dialogIcon" =~ ^[0-9]+$ || "$dialogIcon" -gt 2 ]]; then
local selfServicePath="/Applications/Self Service.app"
local iconName=$(/usr/bin/defaults read "/Applications/Self Service.app/Contents/Info" CFBundleIconFile 2>/dev/null)
dialogIcon="1"
if [[ -n "$iconName" && -r "$selfServicePath/Contents/Resources/$iconName.icns" ]]; then
dialogIcon="alias POSIX file \"$selfServicePath/Contents/Resources/$iconName.icns\""
elif [[ -r "/System/Library/CoreServices/CoreTypes.bundle/Contents/Resources/AlertNoteIcon.icns" ]]; then
dialogIcon="alias POSIX file \"/System/Library/CoreServices/CoreTypes.bundle/Contents/Resources/AlertNoteIcon.icns\""
fi
fi
buttonPressed=$(/usr/bin/sudo -u "$currentUser" /usr/bin/osascript << EOF
tell application "System Events"
try
activate
display dialog "$dialogMessage" with icon $dialogIcon buttons {$dialogButtons} default button $defaultButton giving up after $dialogGiveUp
return (button returned of the result)
end try
end tell
EOF
)
echo "$buttonPressed"
}
# this script must be run with root privileges
if [[ "$(/usr/bin/id -u)" -eq 0 ]]; then
# bootMode is a 2-bit binary value defined as follows:
# the most significant bit (MSB) specifies the actual boot mode. "Recovery" is 0 and "Diagnostics" is 1.
# the least significant bit (LSB) specifies the boot method. 0 means "local" and 1 means "Internet".
# so "01" would set the boot mode to "Internet Recovery" and "10" would boot local diagnostics.
bootMode=
# to set the MSB (as explained above) we ask the users if the
# Mac should be started in recovery or diagnostics mode and
# then set the MSB accordingly.
buttonPressed=$(displayDialog "Would you like to restart your Mac in recovery or diagnostics mode?" "Cancel,Diagnostics,Recovery" "" "" "60")
if [[ "$buttonPressed" = "Recovery" ]]; then
bootMode="0"
elif [[ "$buttonPressed" = "Diagnostics" ]]; then
bootMode="1"
fi
# we go ahead, if the user did not click "cancel" in the previous dialog …
if [[ -n "$bootMode" ]]; then
# … and ask the user if the Mac should be booted from the local
# disk or from Internet. So we can set our LSB now.
buttonPressed=$(displayDialog "Would you like to boot from your Mac's local disk or from the Internet?" "Cancel,Internet,Local" "" "" "60")
if [[ "$buttonPressed" = "Local" ]]; then
bootMode="${bootMode}0"
elif [[ "$buttonPressed" = "Internet" ]]; then
bootMode="${bootMode}1"
fi
# we convert our binary number into an integer and
# select the actual argument for the nvram command
bootArg=
case "$((2#$bootMode))" in
0)
bootArg="RecoveryModeDisk"
;;
1)
bootArg="RecoveryModeNetwork"
;;
2)
bootArg="DiagsModeDisk"
;;
3)
bootArg="DiagsModeNetwork"
;;
esac
if [[ -n "$bootArg" ]]; then
# we ask the user to restart the Mac
buttonPressed=$(displayDialog "Please click \"Restart\" to restart your Mac." "Cancel,Restart" "" "" "60")
if [[ "$buttonPressed" = "Restart" ]]; then
# the user clicked the "Restart" button so we use
# the nvram tool to set the boot options …
log "Setting boot mode to \"$bootArg\""
/usr/sbin/nvram "internet-recovery-mode=$bootArg"
# if the user want to boot into recovery mode, we have to
# make sure the user has admin rights
if [[ "$bootArg" =~ ^Recovery ]]; then
isNotAdmin=$(/usr/bin/dsmemberutil checkmembership -U "$currentUser" -G admin | /usr/bin/grep -i "is not")
if [[ -n "$isNotAdmin" ]]; then
/usr/sbin/dseditgroup -o edit -a "$currentUser" -t user admin
fi
# make sure the Recovery environment is aware the user
# has admin rights, by updating the preboot volume
/usr/sbin/diskutil apfs updatepreboot / >/dev/null 2>&1
fi
# … and restart the machine a few seconds after
# this script exited
(/bin/sleep 5 && /sbin/shutdown -r now) &
fi
fi
fi
else
log "ERROR! You must be root in order to run this script!"
exitCode=1
fi
exit $exitCode

  1. Hinrich
    March 30, 2020 at 8:44 am

    Great!
    Will this even work if the efi firmwarepassword is enabled? Or will it by-passed if set boot args are set via nvram?

    • Wicker
      April 2, 2020 at 1:31 pm

      +1 Hinrich, I would also be very interested to know if it works with Efi set. Thanks.

    • moojomoore
      April 3, 2020 at 6:40 pm

      Just tested it and it looks like it asks for a firmware password. 😦

  2. macbro
    April 3, 2020 at 11:19 pm

    will the elevation of the permissions reset after the user logs back in?

  3. April 8, 2020 at 9:23 pm

    Fantasic Rich. But why should Jamf Pro people have all the fun? I adapted it for Munki Self-Service and added it to my collection of (now 24) items.
    https://github.com/precursorca/Munki-SelfService-On-Demand

  4. txhaflaire
    May 20, 2020 at 3:00 pm

    @wicker i have adopted this method from Rich and slightly modified for the diagnostic part the firmwarepassword it temporariliy removed, before restart/shutdown to diagnostics a LaunchDaemon is being created which unloads and removes itself after booting up again and it triggers an policy to enable the firmwarepassword directly after the mac comes online

  1. No trackbacks yet.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: