Jamf Helper Reboot Script with Deferral

kadams
Contributor

Anyone has a reboot script that Allows deferral and uses Jamf Helper?. Would like to attach it to an apple updates policy. After the policy runs, it should prompt the user to restart the machine. The user should be allowed to postpone that reboot at least once before it reboots on its own. I have seen some that uses CocoaDialog. We cant/wont use Cocoadialog here at our company.

20 REPLIES 20

blackholemac
Valued Contributor III

I have the rudiments of one that does it with AppleScript. It was designed around our company’s previous energy policy. While I wouldn’t use it as is, I can post it later to help with formulation of what you want to do.

Basically it was triggered at 6 pm and would check if the user was working. If not it would shutdown the computer. If so, it would give a preprescribed message from our energy management folks offering to shutdown or defer. I then had a cron script (that’s how old this is) recall it every two hours offering the same choices. It did that every two hours between 6 pm and 6 am.

Again, I wouldn’t use it as is (we don’t even use it anymore) but it might give you food for thought. I’ll post after lunch.

kadams
Contributor

@blackholemac , thanks Im actually brand new to scripting. I need to find a way to get what I posted to work.

m_donovan
Contributor III

Here is one that I use. It checks if the computer has rebooted in the last 4 days. If not a jamfHelper gives them the option of to restart Immediately, 2 mins, 5 mins or 1 hour. The user then gets another jamf helper as a 1 min warning to restart. It is setup on a once a day recurring checkin policy but only executes if the last reboot was at least 4 days ago. The number of days can be edited by editing this line if [ $diffDays -ge 4 ];then to your desired number of days. It also checks for a custom logo we populate on our computers. If not present it will use a built in icon. Test, test, and test again before using in a production environment.

#!/bin/bash

#######################################################################################
#
# Copyright (c) 2016, JAMF Software, LLC.  All rights reserved.
#
#       Redistribution and use in source and binary forms, with or without
#       modification, are permitted provided that the following conditions are met:
#               * Redistributions of source code must retain the above copyright
#                 notice, this list of conditions and the following disclaimer.
#               * Redistributions in binary form must reproduce the above copyright
#                 notice, this list of conditions and the following disclaimer in the
#                 documentation and/or other materials provided with the distribution.
#               * Neither the name of the JAMF Software, LLC nor the
#                 names of its contributors may be used to endorse or promote products
#                 derived from this software without specific prior written permission.
#
#       THIS SOFTWARE IS PROVIDED BY JAMF SOFTWARE, LLC "AS IS" AND ANY
#       EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
#       WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
#       DISCLAIMED. IN NO EVENT SHALL JAMF SOFTWARE, LLC BE LIABLE FOR ANY
#       DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
#       (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
#       LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
#       ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
#       (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
#       SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
#######################################################################################
#
#######################################################################################
#
#           This script will check the last reboot date of the computer. If it has been
#     more than 3 days the user is presented a popup with the option to reboot now,
#     or delay for 2 minutes, 5 minutes, or 1 hour. 1 minute from the planned reboot
#     another popup is presented with a 1 minute countdown.
#
#     Modified by Mike Donovan March 1, 2018
#######################################################################################
# jss.jhp.delay.sh
# ©2016 brock walters jamf
#
#
# the information in the jamfhelper pop-up window can be modified by changing the following below:
#
#   -title
#   -heading
#   -description
#   -icon (eg, a .b64 encoded .png or .icns file in the script or a reference to a graphics file)
#   -button1 (limited characters in field)
#   -button2 (limited characters in field)
#   -showDelayOptions (in seconds)
#
#   for other jamfHelper options see:
#
#      /Library/Application Support/JAMF/bin/jamfHelper.app/Contents/MacOS/jamfHelper -help
#
# the case statement takes input from the jamfHelper button exit code variable "$result"
#
# some command or a series of commands separated by pipes or newlines or other functions
# containing lists of commands could be added to the script & called by replacing "$cmdstr"
# & "$pthstr" with the function names.
###########################################################################################
#
#   Modified for KISD by Mike Donovan March 8, 2018
#
#
###########################################################################################

cleanUp(){
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# CREATE FIRST BOOT SCRIPT
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #

/bin/mkdir /usr/local/jamfps

/bin/echo "#!/bin/bash
## First Run Script to remove the installer.
## Clean up files
/bin/rm -f /Library/LaunchDaemons/org.yourorgname.rebootdelay.plist
/bin/rm -f /Library/LaunchDaemons/org.yourorgname.rebootdelaywarning.plist
/bin/rm -f /Library/Scripts/rebootwarning.sh
launchctl remove org.yourorgname.rebootdelay
launchctl remove org.yourorgname.rebootdelaywarning
/bin/sleep 2
## Update Device Inventory
/usr/local/jamf/bin/jamf recon
## Remove LaunchDaemon
/bin/rm -f /Library/LaunchDaemons/com.jamfps.cleanupDelayedRestart.plist
## Remove Script
/bin/rm -fdr /usr/local/jamfps
exit 0" > /usr/local/jamfps/cleanupDelayedRestart.sh

/usr/sbin/chown root:admin /usr/local/jamfps/cleanupDelayedRestart.sh
/bin/chmod 755 /usr/local/jamfps/cleanupDelayedRestart.sh

# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# LAUNCH DAEMON
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #

cat << EOF > /Library/LaunchDaemons/com.jamfps.cleanupDelayedRestart.plist
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>com.jamfps.cleanupDelayedRestart</string>
    <key>ProgramArguments</key>
    <array>
        <string>/bin/bash</string>
        <string>-c</string>
        <string>/usr/local/jamfps/cleanupDelayedRestart.sh</string>
    </array>
    <key>RunAtLoad</key>
    <true/>
</dict>
</plist>
EOF

##Set the permission on the file just made.
/usr/sbin/chown root:wheel /Library/LaunchDaemons/com.jamfps.cleanupDelayedRestart.plist
/bin/chmod 644 /Library/LaunchDaemons/com.jamfps.cleanupDelayedRestart.plist
}

lastBootRaw=$(sysctl kern.boottime | awk -F'[= |,]' '{print $6}')

lastBootFormat=$(date -jf "%s" "$lastBootRaw" +"%m-%d-%Y")


today=$(date +%s)
#today=$(date -v+4d +%s) ###########For Testing #############################################

diffDays=$(( (today - lastBootRaw) / 86400 ))

#echo $diffDays

if [ $diffDays -ge 4 ];then

    #echo "4 days or more Running Reboot script"
    file=$(find /Library/Application Support/JAMF/bin/KISDColorseal.png)
    if [ ! -z "$file" ]
    then
        useIcon=/Library/Application Support/JAMF/bin/KISDColorseal.png
        #echo "found"
    else
        useIcon=/System/Library/CoreServices/CoreTypes.bundle/Contents/Resources/AlertCautionIcon.icns
        #echo "not found"
    fi

else
    #echo "3 days or less Exiting"
    exit 0
fi

jamfhelper()
{
/Library/Application Support/JAMF/bin/jamfHelper.app/Contents/MacOS/jamfHelper 
-windowType utility 
-title "KISD Mac Systems Admin" 
-heading "It's time to reboot" 
-description "This computer has not restarted since $lastBootFormat. Restart now or choose a delay option." 
-icon "$useIcon" 
-iconSize 110 
-button1 "Delay" 
-button2 "Restart Now" 
-showDelayOptions "120, 300, 3600" # 2 minutes, 5 minutes, 1 hour
}

# variables
result=$(jamfhelper)
delayint=$(echo "$result" | /usr/bin/sed 's/.$//')
warndelayint=$(expr $delayint - 60)
#echo $delayint
#echo $warndelayint
defercal=$(($(/bin/date +%s) + delayint))
hour=$(/bin/date -j -f "%s" "$defercal" "+%H")
minute=$(/bin/date -j -f "%s" "$defercal" "+%M")
#echo $hour
#echo $minute
warndefercal=$(($(/bin/date +%s) + warndelayint))
warnhour=$(/bin/date -j -f "%s" "$warndefercal" "+%H")
warnminute=$(/bin/date -j -f "%s" "$warndefercal" "+%M")
#echo $warnhour
#echo $warnminute

# write launch daemon populated with variables from jamfHelper output

delay()
{
/bin/cat <<EOF > /Library/LaunchDaemons/org.yourorgname.rebootdelay.plist
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
  "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>org.yourorgname.rebootdelay</string>
    <key>ProgramArguments</key>
    <array>
        <string>reboot</string>
    </array>
    <key>StartCalendarInterval</key>
    <dict>
        <key>Hour</key>
        <integer>$hour</integer>
        <key>Minute</key>
        <integer>$minute</integer>
    </dict>
</dict>
</plist>
EOF
}

warndelay()
{
/bin/cat <<EOF > /Library/LaunchDaemons/org.yourorgname.rebootdelaywarning.plist
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
  "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>org.yourorgname.rebootdelaywarning</string>
    <key>ProgramArguments</key>
    <array>
        <string>sh</string>
        <string>/Library/Scripts/rebootwarning.sh</string>
    </array>
    <key>StartCalendarInterval</key>
    <dict>
        <key>Hour</key>
        <integer>$warnhour</integer>
        <key>Minute</key>
        <integer>$warnminute</integer>
    </dict>
</dict>
</plist>
EOF
}

warnScript()
{
/bin/cat <<EOF > /Library/Scripts/rebootwarning.sh
#!/bin/bash

/Library/Application Support/JAMF/bin/jamfHelper.app/Contents/MacOS/jamfHelper 
-windowType utility 
-title "KISD Systems Admin" 
-heading "It's time to reboot" 
-description "This computer is set to reboot in 1 minute." 
-timeout 60 
-countdown 
-lockHUD 
-icon "$useIcon" 
-iconSize 110 
-button1 "Ok"

EOF
}

finalPrep()
{
# unload launchd
#launchctl unload /Library/LaunchDaemons/org.yourorgname.rebootdelay.plist
#launchctl unload /Library/LaunchDaemons/org.yourorgname.rebootdelaywarning.plist

# set ownership on delay launch daemon
chown root:wheel /Library/LaunchDaemons/org.yourorgname.rebootdelay.plist
chmod 644 /Library/LaunchDaemons/org.yourorgname.rebootdelay.plist

# set ownership on delaywarning launch daemon
chown root:wheel /Library/LaunchDaemons/org.yourorgname.rebootdelaywarning.plist
chmod 644 /Library/LaunchDaemons/org.yourorgname.rebootdelaywarning.plist

#load launchd
launchctl load /Library/LaunchDaemons/org.yourorgname.rebootdelay.plist
launchctl load /Library/LaunchDaemons/org.yourorgname.rebootdelaywarning.plist

}

# select action based on user input

case "$result" in
    *1 )    delay
            warndelay
            warnScript
            finalPrep
            cleanUp
            ;;
    *2 )    reboot
            echo "Reboot Called"
            ;;
esac

exit 0

Do you have an updated script for Catalina, bigsur. was trying to get this working, 

How can i edit this script to change the final reboot warning timer for 1 minute to 15 minutes ? 
Seems like you can't just edit a line when using these jamf helper scripts. 
Any help would be appreciated

blackholemac
Valued Contributor III

by default I like @m.donovan 's script better as I never really liked mine, but it got the job done. I don't use mine anymore because I was able to persuade our energy management czar that full shutdowns as opposed to sleep don't really save power. That being said, I keep it just in case I can't win that argument. If it helps great, if nothing else, I have @m.donovan 's script to use if I'm ever forced to consider a shutdown script again.

#!/bin/sh
#######################DEFINE VARIABLES AND READ-IN PARAMETERS############################

minutesN=5
# Number of minutes to count down before shutdown

shutdownAction="shutdown -h now"
# Change to "shutdown -r now" to reboot
# Change to "shutdown -h now" to shut down

notificationMessage="ATTENTION!!!!    ATTENTION!!!!    ATTENTION!!!!

In an effort to conserve energy during evenings and weekends, this Mac is scheduled to shut down in five minutes.

Please save all open documents and close any open applications prior to a shut down."
# This message will appear in the initial dialog box following the initial warning dialog.

shutdownButtonText="Shut Down NOW!"
# This variable should contain either "Shut Down" or "Restart"
# depending on the value of $shutdownAction.  This string will appear
# in the dialog and will determine the name of the button that causes
# $shutdownAction to be executed.

shutdownTitle="LSC Automated Shutdown"
# You can customize this to your organization as you see fit.

postponeButtonText="Postpone for 2 Hours"
# Basically this button could say anything as it causes the script to exit 0 when pushed.

openbusinessHour="6"
closebusinessHour="18"
# You would use military time by hour here, for example 6 = 6 a.m., 18 = 6 p.m.

###################################SCRIPT CONTENTS########################################

rm -f /var/log/newshutdowndebug*

# exit 0 if this script accidentally runs during declared business hours
current_hour=`/bin/date +%H`
if [ $current_hour -gt $openbusinessHour -a $current_hour -lt $closebusinessHour ]; then
    exit 0
fi

# If no user is logged in at the console, shut down immediately
consoleUser=`/usr/bin/w | grep console | awk '{print $1}'`
if test "$consoleUser"  == ""; then
    $shutdownAction
fi

function timedShutdown {
button=`/usr/bin/osascript << EOT
try
    with timeout of 400 seconds
        tell application "Finder"
            activate
            set question to display dialog "$notificationMessage" with title "$shutdownTitle" buttons {"$postponeButtonText", "$shutdownButtonText"} default button 1 giving up after 300
            set answer to button returned of question
            return answer
        end tell
    end timeout
on error
    return "error"
end try
EOT`

if test "$button" == "$postponeButtonText"; then
    exit 0
elif test "$button" == "$shutdownButtonText"; then
    $shutdownAction
elif test "$button" == "error"; then
    exit 0
else
    $shutdownAction
    exit 0
fi
}

timedShutdown

kadams
Contributor

Hey @mdonovan , I would need your script to run every day with our apple updates policy. Would I be able to configure this to do that? i know you mentioned we can change the days from 4 to anything else.

kadams
Contributor

I tested @mdonovan script in terminal. It doesn't seem to be working for me. Am I testing it incorrectly?

kadams
Contributor

Tested both of these and cant get any of them to run. Nothing happens at all.

m_donovan
Contributor III

When you test this be sure to change these lines as shown. The # comments out the line starting with it. This simulates a reboot older than 4 days. Otherwise the computers actual last reboot date is used. Also you will need to run it as root so if running from the desktop of your test machine I use terminal and enter sudo bash /path/to/file.sh

#today=$(date +%s)
today=$(date -v+4d +%s) ###########For Testing #############################################

kadams
Contributor

@m.donovan. If i want this to execute immediately after running a policy, would i change the days from 4 to 0?

m_donovan
Contributor III

The 4 is used in the difference variable to decide the allowed number of days to go without a reboot. If you use the today=$(date +%s) line as is and set the diffDays comparison to 0 then it should execute every time this script is run. So if used in a policy that is on a once per day trigger the user should be prompted to restart once everyday. However, I don't think this going to happen at the same time everyday. You may get users that are prompted to restart at 3pm Monday and again at 7AM on Tuesday. You should really do some testing to see how this will impact your users.

kadams
Contributor

@mdonovan , what im needing is starting to become a lot more difficult. I need a script that restarts computers after apple updates. All apple updates do not require a reboot. So I need a script to only execute when an apple updates requires a restart. I would need the script to execute every time the apple updates script has run.

mm2270
Legendary Contributor III

@kadams Maybe I missed it, but how are the Apple updates you're referring to being installed? Do you have a separate script that's installing those updates, and, are they first being cached from a Jamf policy, and then installed, or are you installing them using a softwareupdate command?

So you know, you can get the restart flag from a package using the installer command. For example:

/usr/sbin/installer -query RestartAction -pkg /path/to/Package.pkg

The above will return a few different string possibilities, including

None   (Means no reboot required)
RecommendRestart
RequireLogout
RequireRestart  (Most common one for pkgs that require a restart, like most security updates, etc)
RequireShutdown

Using the above, if you are caching these installers, then installing them in a script, you should be able to query for the restart action on each pkg and if any require or recommend restart, set a variable that would then initiate your reboot after the script completes the installs successfully.
Conversely, if none of the updates to be installed require or recommend a reboot, logout or other action, then don't do anything after they are all installed.

Hopefully that makes some sense.

kadams
Contributor

@mm2270 We have this script thrown in a policy sudo sh -c "softwareupdate -ia".

crskerman
New Contributor II

@m.donovan I've been playing with your script and really like the way it works. However have found that users can just ignore the initial dialog box, move it out of the way at the bottom of the screen and continue on for days. Is there a way to make it time out and just restart if no options are selected?

jkupidura
New Contributor

I know this is a little old but this is the script I am using for applying forced updates. It will automatically time out after 5 minutes and choose to delay the update if the user ignores the window. You can also choose your deferral amounts by changing a $maxdeferrals number to whatever you would like. It does delay one hour each deferment though which can be changed with a few edits. Once the counter expires or they select restart now, then the update command executes. Hope this helps.

#!/bin/bash

#set deferral counter to zero
deferral=0

#set max amount of deferments a users can make
maxdeferrals=3


choice="2"

#edit title and msg that you want displayed in the pop up window
TITLE="Restart required"
MSG="Software Updates are ready to be installed on your Mac that require a restart.

You can choose to restart now, or delay this restart by 1 hour by choosing the option below:"



#Launches pop up
while [ "$choice" -ne "0" ]
    do

    #You can choose which icon you would like to appear in the pop up here
    ICON="/System/Library/CoreServices/Software Update.app/Contents/Resources/SoftwareUpdate.icns"

    choice=$(/Library/Application Support/JAMF/bin/jamfHelper.app/Contents/MacOS/jamfHelper -windowType utility -title "$TITLE" -description "$MSG" -icon "$ICON" -button1 "Restart Now" -button2 "Delay 1h" -defaultButton 2 -lockHUD -timeout 300)


    #adds one to the deferral counter
    if [ "$choice" -eq "2" ];
        then ((deferral+=1))
        fi


    if [ "$choice" -ne "0" ] && [ "$deferral" -lt "$maxdeferrals" ];

        then sleep 3600 
        else ((choice = "0"))
        fi

    done


softwareupdate -ia --restart

JeyT
New Contributor III

@jkupidura, I am testing with your script, thanks. What I am looking to do is give the user an option to defer for 2 hours each time with a maximum of 12 deferrals (24 hours) and then the update will force run. New to scripting too. So what I need to do is:

  1. maxdeferrals=12 (change from 3 to 12)
  2. then sleep changes from 3600 to 7200.

I don't understand the "It does delay one hour each deferment though which can be changed with a few edits." What gets delayed one hour? I thought when you click on the number 2 button it will defer for whatever your value is in the "sleep"?

Appreciate any help.

Rohitds14
New Contributor III

@JeyT did you test the script with 24 hours deferral? i also wondering to test that

JeyT
New Contributor III

@Rohirtds14 I have not. Waiting on the release of the new deferral feature in 10.21. Hope that fixes this. Thanks