Posted on 08-21-2014 11:04 AM
I've posted a version of this script before but did some updates and wanted to re-post. This script will grab the uptime of a machine, and if past a set minimum number of days, will use cocoaDialog to bug the user to restart. If the machine is up past a set maximum number of days, the script will bug them with cocoaDialog and will start sending emails to them.
The script uses the JSS API (thanks to @brysontyrrell for his article on the JSS API) to grab the user's real name and email address to send them a message.
You can find the script on Github here: https://github.com/stevewood-tx/CasperScripts-Public/tree/master/checkUpTime
The link to Bryson's article: http://bryson3gps.wordpress.com/2014/03/30/the-jss-rest-api-for-everyone/
And here's the script:
#!/bin/sh
# Name: checkUpTime.sh
# Date: 19 Aug 2014
# Author: Steve Wood (swood@integer.com)
# Purpose: look for machines that have not been restarted in X number of days.
# Requirements: cocoaDialog on the local machine
#
# How To Use: create a policy in your JSS with this script set to run once every day.
## Global Variables and Stuff
logPath='/path/to/store/log/files' ### <--- enter a path to where you store log files locally
if [[ ! -d "$logPath" ]]; then
mkdir $logPath
fi
set -xv; exec 1> $logPath/checkUpTime.txt 2>&1
version=1.0
CD="/path/to/cocoaDialog.app/Contents/MacOS/cocoaDialog" ### <--- path to where you store cocoDialog on local machine
NC='/Library/Application Support/JAMF/bin/Management Action.app/Contents/MacOS/Management Action'
jssURL='https://YOUR.JSSSERVER.COM:8443' ### <--- enter your JSS URL
apiUser="<APIREADUSER>" ### <--- enter your API user
apiPass="<APIREADUSERPASS>" ### <--- enter your API user password
serNum=$(ioreg -l | grep IOPlatformSerialNumber | awk '{print $4}'| sed 's/"//g')
cdTitle="Machine Needs A Restart"
loggedInUser=`/bin/ls -l /dev/console | /usr/bin/awk '{ print $3 }'`
## set minDays - we start bugging users at this level with just a dialog box
minDays=7
## set maxDays - after we reach maxDays we bug with dialog box AND email
maxDays=15
## Grab user info ##
### Thanks to Bryson Tyrrell (@bryson3Gps) for the code to parse
info=$(curl -s -k -u $apiUser:$apiPass $jssURL/JSSResource/computers/match/$serNum)
email=$(echo $info | /usr/bin/awk -F'<email>|</email>' '{print $2}')
realName=$(echo $info | /usr/bin/awk -F'<realname>|</realname>' '{print $2}')
#### MAIN CODE ####
days=`uptime | awk '{ print $4 }' | sed 's/,//g'` # grabs the word "days" if it is there
num=`uptime | awk '{ print $3 }'` # grabs the number of hours or days in the uptime command
## set the body of the email message
message1="Dear $realName"
message1b="Your computer has now been up for $num days. It is important for you to restart your machine on a regular"
message2="basis to help it run more efficiently and to apply updates and patches that are deployed during the login or logout"
message3="process."
message3a="Please restart your machine ASAP. If you do not restart, you will continue to get this email and the pop-up"
message4="dialog box daily until you do."
message5="FROM THE IT STAFF" ### <--- change this to whomever you want
## now the logic
if [ $loggedInUser != "root" ]; then
if [ $days = "days" ]; then
if [ $num -gt $minDays ]; then
if [ $num -gt $maxDays ]; then
cdIcon="/private/var/inte/icons/redX.icns"
cdText="Your computer has not been restarted in more than $maxDays days. Please restart ASAP. Thank you."
bubble=`$CD bubble --title "$cdTitle" --no-timeout --text "$cdText" --icon-file $cdIcon`
if [ $email != "" ]; then
echo "$message1
$message1b
$message2
$message3
$message3a
$message4
$message5" | mail -s "URGENT: Restart Your Machine" $email
fi
else
cdIcon="/private/var/inte/icons/ProblemReporter.icns"
cdText="Your computer has not been restarted in $num days. Please restart ASAP. Thank you."
bubble=`$CD bubble --title "$cdTitle" --no-timeout --text "$cdText" --icon-file $cdIcon`
fi
fi
fi
fi
exit 0
Posted on 08-21-2014 01:20 PM
Good stuff man, thanks for posting.
Posted on 08-22-2014 02:37 AM
Hey all,
This is pretty cool.
I just ran a test and went to check the log file the script creates and there in plain text is the API user name and password.
Is there a way to stifle this? Is the log file necessary?
Thanks as always,
-pat
-pat
Posted on 08-22-2014 04:29 AM
Ok. Was able to comment out lines in order to stop the log file.
### logPath='/Library/Logs' ### <--- enter a path to where you store log files locally
### if [[ ! -d "$logPath" ]]; then
### mkdir $logPath
### fi
### set -xv; exec 1> $logPath/checkUpTime.txt 2>&
Which is fine for me cause I really don't need to the log file. At least I don't think I do.
Then another error pops up, which has no bearing on the log file. Cause I was seeing the error before I commented the above lines:
Script result: /Library/Application Support/JAMF/tmp/checkUptime.sh: line 66: [: !=: unary operator expected
Which points to:
if [ $email != "" ]; then
echo "$message1
$message1b
$message2
$message3
$message3a
$message4
$message5" | mail -s "URGENT: Restart Your Machine" $email
fi
I can comment the above lines out and the error goes away. But I do like the idea of email the user.
Thanks again!!
-pat
Posted on 08-22-2014 06:36 AM
Just FYI, you don't need to hardcode the API username and password into a script. You can assign them to parameters, like $4 and $5 respectively, and pass that down to the script from the JSS when it runs. That's always what I do. That way, if someone actually gets their hands on the script they still wouldn't see a username and password in it.
Regarding the unary operator error, there are two things you can try changing to fix that.
The first if to use double brackets, like so
if [[ $email != "" ]]; then
or, quote the $email variable
if [ "$email" != "" ]; then
Either one may help stop that error.
Posted on 08-22-2014 06:54 AM
@mm2270 you're absolutely right. I didn't pass it as a $ variable for no real reason. Just being lazy, I guess. :-)
As for the unary operator error, I'll probably just change it to this:
if [ $email ]; then
Since really all I am testing for is if the email address is blank.
Posted on 08-22-2014 07:34 AM
I just tried using the $4 and $5 and yes, it does work!!
For the email part, I've tried both ways:
if [[ $email != "" ]]; then
or
if [ "$email" != "" ]; then
And both no longer produce the error I posted.
But I don't receive the email even though my machine is 32 days uptime.
-pat
Posted on 08-22-2014 07:46 AM
@pvader I'm assuming your email address is filled out in the JSS for your computer record, correct? You can troubleshoot by checking the mail log on your machine. It is located in /var/log/mail.log. Check to see if the email is getting sent out or if an error is popping up.
Posted on 08-25-2014 06:52 AM
I do have a correct email entry that computer record. So that is good.
I do not have /var/log/mail.log on the client machine nor the JSS server (which is an Ubuntu 12.04 flavour.)
-pat
Posted on 08-25-2014 07:18 AM
@pvader - you should probably throw an extra echo in there to print out what email address the script is pulling. Are you certain you have your API account set up correctly? You can easily test it in a browser with the same credentials ahead of time to be sure there are no problems with that.
As an alternative, if your user accounts are all primarily AD based, you could simply grab the email address from the locally cached account, based on either who is logged in at the moment, or who's the most frequently logged in account. The 'EMailAddress' attribute gets stored in AD based accounts on the local system and can be pulled pretty easily with dscl.
Posted on 08-28-2014 01:51 AM
I'll check it soon. Busy with Universal Type Client 4 and Adobe CC roll out these past few days.
Thanks all as always!
-p
Posted on 01-17-2019 08:31 AM
Can it pop up a message window instead of sending e-mail?
Posted on 01-17-2019 02:31 PM
@jchin-ro
- We use an EA to tell us Days Since Last Reboot.
- We then create a smart group for machines with 14+ days since last reboot. In the criteria, we excluded servers.
- We then created a script using displayMessage that will put up a dialog that says "Just a friendly reminder that your Mac hasn't been restarted in a while. Please restart when you get a chance." - and requires the user click "OK" to acknowledge.
- We then ccreated a policy that calls the script and scoped it to the above smart group and set it to trigger on recurring checkin once a week.
So once a machine hasn't been rebooted in 14 days, the user will get a weekly reminder that they have to acknowledge until they reboot - causing their machine to leave the smart group.
Posted on 01-17-2019 02:34 PM
I would love to do exactly the same thing. Can you shed a little more detail on how to do that display message setup? I am new to Jamf and any pointers would really help. Thanks.
Posted on 01-17-2019 05:33 PM
Have a look at :
https://www.jamf.com/jamf-nation/feature-requests/751/jamfhelper-make-it-awesome
https://github.com/haircut/better-jamf-policy-deferral
I might suggest running such a policy after hours initially, or at least detect Power Point or Keynote running. (this can be complicated if they're set to launch on startup)
Also be sure to check you're collecting inventory sufficiently, keeping in mind that inventory collection doesn't always succeed on startup, you can end up with a client that doesn't leave the smart group, even after a reboot. To be safe, script uptime "precheck" before you kick off notify as in the original post.
Posted on 01-17-2019 07:22 PM
@jchin-ro Here's the EA for days since last reboot.
#!/bin/bash
# Commands required by this script
# credit to acidprime on jamfnation
declare -x awk="/usr/bin/awk"
declare -x sysctl="/usr/sbin/sysctl"
declare -x perl="/usr/bin/perl"
declare -xi DAY=86400
declare -xi EPOCH="$($perl -e "print time")"
declare -xi UPTIME="$($sysctl kern.boottime |
$awk -F'[= ,]' '/sec/{print $6;exit}')"
declare -xi DIFF="$(($EPOCH - $UPTIME))"
if [ $DIFF -le $DAY ] ; then
echo "<result>1</result>"
else
echo "<result>$(($DIFF / $DAY))</result>"
fi
Here's the script for pushing the reminder message to the user -
#!/bin/sh
/usr/local/jamf/bin/jamf displayMessage -message "Just a friendly reminder that your Mac hasn't been restarted in a while. Please restart when you get a chance."
We collect inventory once a day for all endpoints.
We scope the policy for the notification message to run once a week, and exclude servers.
So given these two things, it's very unlikely (but not impossible) that we'll encounter a situation where a user just rebooted right before getting the notification.
If this is something you want to do, it's easy enough to test the EA and the script to push the notification separately.
All that said. . .if you wanted to go nuts you could probably use the same EA, smart groups, and logic and do a more elegant Notification Center notification using Yo (https://github.com/sheagcraig/yo). We just haven't gone there yet and implemented this more quickly.
Posted on 01-17-2019 09:15 PM
easier to get uptime in days with this one liner :
#!/bin/sh
uptime | cut -d "," -f 1 | awk '{print $3;}'
I might make the message less arbitrary and include something along the lines "In order to ensure installation of security/application updates and to improve performance, please reboot as soon as convenient"
Maybe start turning this on for clients that haven't rebooted in 30, and evaluate from there. Starting at even 14 will likely cause disruption and push back from a large group of users.
Posted on 01-18-2019 07:23 AM
@lkrasno Your uptime script is definitely simpler. How does it handle displaying a value of less than one?
One of the reasons we use this particular methodology is that it shares a foundation with a few scripts/EAs we have that monitor things that use epoch time.
As for the message, the verbiage can definitely be whatever suits the personality and culture of your org. We've had this workflow in place for over a year across all of our clients (we're an MSP) and we've heard no complaints from anyone regarding frequency.
Posted on 01-18-2019 10:04 AM
@jchin-ro if you look at the original post, I was using cocoaDialog to pop a bubble (Notification Center) dialog. If you have cocoaDialog installed and do not want to have an EA and a policy, you can use just the script in a policy. And the reason I was grabbing the uptime this way:
days=`uptime | awk '{ print $4 }' | sed 's/,//g'` # grabs the word "days" if it is there
num=`uptime | awk '{ print $3 }'` # grabs the number of hours or days in the uptime command
Was because if the machine had been up for 7 hours the script would see that as 7 days unless you checked for the word 'days' and would alert the user.
Cleaned up without email or logging:
#!/bin/sh
CD="/usr/local/bin/cocoaDialog.app/Contents/MacOS/cocoaDialog"
cdTitle="Machine Needs A Restart"
loggedInUser=`/bin/ls -l /dev/console | /usr/bin/awk '{ print $3 }'`
## set minDays - we start bugging users at this level with just a dialog box
minDays=7
## set maxDays - after we reach maxDays we bug with dialog box AND email
maxDays=15
#### MAIN CODE ####
days=`uptime | awk '{ print $4 }' | sed 's/,//g'` # grabs the word "days" if it is there
num=`uptime | awk '{ print $3 }'` # grabs the number of hours or days in the uptime command
## now the logic
if [ $loggedInUser != "root" ]; then
if [ $days = "days" ]; then
if [ $num -gt $minDays ]; then
if [ $num -gt $maxDays ]; then
cdIcon="/System/Library/CoreServices/CoreTypes.bundle/Contents/Resources/AlertStopIcon.icns"
cdText="Your computer has not been restarted in more than $maxDays days. Please restart ASAP. Thank you."
bubble=`$CD bubble --title "$cdTitle" --no-timeout --text "$cdText" --icon-file $cdIcon`
else
cdIcon="/System/Library/CoreServices/CoreTypes.bundle/Contents/Resources/AlertCautionIcon.icns"
cdText="Your computer has not been restarted in $num days. Please restart ASAP. Thank you."
bubble=`$CD bubble --title "$cdTitle" --no-timeout --text "$cdText" --icon-file $cdIcon`
fi
fi
fi
fi
exit 0
Posted on 10-23-2019 07:00 AM
Thanks for creating this thread @stevewood . I am going to use this as a baseline for what my original Google search was (which lead me here) to be able to email users who's Mac hasn't checked in to JAMF in over 30+ days. I know there has got to be a way!
Posted on 02-12-2020 02:06 AM
@amartin253, you can use mailsend-go for that. smtp command line tool.