Posted on 09-16-2019 08:42 AM
Hello,
So I am trying to use Jamf Helper to give our users some flexibility as to when they want to upgrade to Office 2019. My main concern is not forcing the upgrade when they are using the apps, and either the upgrade fails because apps are open, or force quitting of the apps causing loss of unsaved work.
So I thought Jamf Helper might be the way to go.
Below is the script I am using, and a screen shot of the utility window.
Question: How do I actually get the delay to do something? Right now it just ends the attempt with an error that the policy failed. Ideally I'd like the user to keep snoozing the window until they are ready to install.
#!/bin/bash
####################################################################################################
#
# Copyright (c) 2013, JAMF Software, LLC. All rights reserved.
#
# This script was written by the JAMF Software Profesional Services Team
#
# 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.
#
#####################################################################################################
#
# SUPPORT FOR THIS PROGRAM
#
# This program is distributed "as is" by JAMF Software, Professional Services Team. For more
# information or support for this script, please contact your JAMF Software Account Manager.
#
#####################################################################################################
#
# ABOUT THIS PROGRAM
#
# NAME
# jamfHelperByPolicy.sh
#
# SYNOPSIS - How to use
# Run via a policy to populate JAMF Helper with values to present messages to the user.
#
# DESCRIPTION
#
# Populate script parameters to match the variables below.
# Pass in values into these parameters during a policy.
#
####################################################################################################
#
# HISTORY
#
# Version: 1.0
#
# - Created by Douglas Worley, Professional Services Engineer, JAMF Software on May 10, 2013
#
####################################################################################################
# The recursively named JAMF Helper help file is accessible at:
# /Library/Application Support/JAMF/bin/jamfHelper.app/Contents/MacOS/jamfHelper -help
windowType="" # [hud | utility | fs]
windowPosition="" # [ul | ll | ur | lr]
title="" # "string"
heading="" # "string"
description="" # "string"
icon="" # path
iconSize="" # pixels
timeout="" # seconds
[ "$4" != "" ] && [ "$windowType" == "" ] && windowType=$4
[ "$5" != "" ] && [ "$windowPosition" == "" ] && windowPosition=$5
[ "$6" != "" ] && [ "$title" == "" ] && title=$6
[ "$7" != "" ] && [ "$heading" == "" ] && heading=$7
[ "$8" != "" ] && [ "$description" == "" ] && description=$8
[ "$9" != "" ] && [ "$icon" == "" ] && icon=$9
[ "$10" != "" ] && [ "$iconSize" == "" ] && iconSize=$10
[ "$11" != "" ] && [ "$timeout" == "" ] && timeout=$11
HELPER`"/Library/Application Support/JAMF/bin/jamfHelper.app/Contents/MacOS/jamfhelper" -windowType "$windowType" -windowPosition "$windowPosition" -title "$title" -heading "$heading" -description "$description" -icon "$icon" -iconSize "$iconSize" -button1 Install Now” -button2 Snooze -defaultButton 2 -cancelButton "2" -countdown "$timeout" -timeout "$timeout" -showDelayOptions "900, 1800, 3600, 7200, 14400"`
echo "jamf helper result was $HELPER"
if [ "$HELPER" == "0" ]; then
/usr/bin/jamf policy -trigger office2019
exit 0
else
echo "user chose No";
exit 1
fi
Posted on 09-16-2019 09:05 AM
It may not be spelled out here in the script, but it's clear the intention of the use of this was that you would have a dual policy type configuration at work here. Basically, the policy that runs this script would only run the script itself and note the button clicked. If you look at this last section of the script, it calls on another policy to actually install the Office 2019 updates, using a custom event trigger - /usr/bin/jamf policy -trigger office2019
if [ "$HELPER" == "0" ]; then
/usr/bin/jamf policy -trigger office2019
exit 0
else
echo "user chose No";
exit 1
fi
So to make this all work, you need to have a policy scoped to the appropriate Macs that is set to Ongoing
for the execution frequency, but with no trigger other than the custom event one set up. Make sure that what you enter for the custom trigger string is reflected in the script in the line above (replace office2019
with your string as needed)
When the main policy runs that calls this script, IF the user clicks the "Install" button, it then calls that second Ongoing policy to do the installation. Otherwise it just exits. So in other words, the second policy is the workhorse. The visible one to the user is just a prompt. When set up this way, the policy shouldn't record as a failure when the user clicks "Snooze"
As for when that policy pops up again after the snooze period, that kind of depends on you, and your needs. You could for example, make it so that the policy calling the script runs once per day (or once per week if feeling generous), which seems reasonable. You could set it up to run once per check-in, but of course that would mean your clients could get nagged about doing the updates every 5 or 15 minutes or whatnot. May not be what you want, but it's an option.
One last thing to keep in mind. The way the script above is designed means the end users can snooze indefinitely. They'll keep getting that pop up once a day or however often you set it up to run, assuming they are still in scope for the updates, but there is no mechanism to eventually enforce it once they've snoozed it too many times. It goes beyond the scope of this post, but you could look at some existing examples on other threads on how to incorporate a counter. So when they've snoozed it, say, 3 or 4 times, on the next run it doesn't give them the option to snooze anymore and enforces the updates.
Edit: OK, one last thing (for real this time!) I just noticed the script does an exit 1
if the user clicks snooze, which I would probably change to exit 0
. The "exit 1" is what is likely causing it to register as a failure in the policy logs. Jamf Pro will recognize the exit code. I don't see a reason it needs to exit as a failure if it's used in the way I mentioned above.
Posted on 09-16-2019 11:25 AM
Thanks for the detailed response @mm2270. The "office2019" custom trigger is mine, for an Office 2019 policy set to "Ongoing", just as you mentioned. The "Install" button for this works like a champ, and I am able to get the Office 2019 policy to run, and Office to install. Sorry for not being more detailed in regards my results with that part of the script.
Have you seen any examples where the Snooze variable the user selects (15, 30, 60 minutes, etc.) actually determines when the script policy runs again, as opposed to just exiting the window, and the script policy running again based upon the triggers of your policy (once a day, for instance, as you mentioned)? I'm not seeing that the snooze drop down variables actually function in any meaningful way.
Posted on 09-16-2019 11:36 AM
Also, while I am thinking about it, let's say we take away the drop down variables, and just have an Install button, and a "Snooze" button that exits the window.
If we have the jamfHelper script policy set as "Once Every Day", how do we unscope the device from this policy, once the user has clicked "Install" to run the actual "Office 2019" policy?
Posted on 09-16-2019 11:57 AM
@dtmille2 Just comparing your version of the script to the original "jamfHelperByPolicy.sh", it looks like you've added on the "-showDelayOptions". These aren't actually triggering anything, they are just being presented to the user. So, when the user selects a deferral time and hits snooze, the script will do an "exit 1" which essentially is telling you the update failed because the user snoozed it. The script isn't actually telling Jamf to re-run the policy in the deferral time period the user selected from the list.
What I would maybe do is at first set the policy's execution frequency to be once per day, and just have "-showDelayOptions 86400'" , then if need be you can change the execution frequency to ongoing, and assuming your check in frequency is 15 minutes, change delay options to "-showDelayOptions 900'" .... Or just remove the -showDelayOptions completely and just have a snooze button.
The scope of the policy would be to a smart computer group with criteria as "Office 2019 apps not installed" using "Application Version." Just make sure part of your custom trigger policy to install Office 2019 has an inventory update configured under the maintenance payload so that the machine leaves that smart group once the policy has ran, and therefore receives no further update prompts.
Posted on 09-16-2019 12:36 PM
I guess I did get that delay part of the script from somewhere else, @amccarty, probably from a thread or page on the topic.
Thanks for the suggestions! That was just what I needed.
Posted on 09-16-2019 12:56 PM
Hey @dtmille2 , @amccarty is correct, as you've already figured out. I wasn't paying close attention before and did not see that you included those delay options values to the script, which as he stated, doesn't do anything unless you act on the input from the user. They are GUI options, but they do get sent back in the script output.
I also think the best thing would be to just have the script pop up the message once per day in a policy set to a once per day frequency. And they will get prompted each day to do the install and continue until it's done. Using the proper Smart Group scoping for this is essential since you'd have it set to Ongoing in the above case.
Outside of this, it IS possible to use those delay options in jamfHelper and do something with them, but it involves some fancier scripting. I can post an example of a modification to what you have to get you that capability, but I don't know if I'd be muddying the waters in doing so. You may be better off just keeping it simple as explained above. But post back if you really had your heart set on allowing the user to choose a more custom delay time.
Posted on 09-16-2019 01:03 PM
@mm2270, I would love seeing an example of a modification to use the delay data. I will probably go the simpler route, however, so don't bother yourself if you have better things to do! ;‑)
Posted on 09-16-2019 01:51 PM
Sure. I don't mind posting that. I was able to graft something onto the script you posted above, just so it's easier to see where the modifications are.
The first thing to understand about using -showDelayOptions
in jamfHelper is that it changes the output you get back in the shell. Normally, a regular jamfHelper window that doesn't use any drop down options will give you a 0
for the main button clicked and a 2
if the second button was clicked (if configured in the script) But when you add those drop down options, those change in 2 ways. First, the response for buttons clicked changes to 1
and 2
instead of 0
and 2
The second thing is, the value from the drop down in seconds gets smooshed together with the button clicked value into one string. So in the case of choosing 15 minutes, in Terminal you would get back 9002
, meaning, 900 seconds for the value from the drop down and 2 for the button chosen. So you have to tease those values apart from each other to be able to do anything with it later.
Here's a modified script that will do something with those values.
#!/bin/bash
####################################################################################################
#
# Copyright (c) 2013, JAMF Software, LLC. All rights reserved.
#
# This script was written by the JAMF Software Profesional Services Team
#
# 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.
#
#####################################################################################################
#
# SUPPORT FOR THIS PROGRAM
#
# This program is distributed "as is" by JAMF Software, Professional Services Team. For more
# information or support for this script, please contact your JAMF Software Account Manager.
#
#####################################################################################################
#
# ABOUT THIS PROGRAM
#
# NAME
# jamfHelperByPolicy.sh
#
# SYNOPSIS - How to use
# Run via a policy to populate JAMF Helper with values to present messages to the user.
#
# DESCRIPTION
#
# Populate script parameters to match the variables below.
# Pass in values into these parameters during a policy.
#
####################################################################################################
#
# HISTORY
#
# Version: 1.0
#
# - Created by Douglas Worley, Professional Services Engineer, JAMF Software on May 10, 2013
#
####################################################################################################
# The recursively named JAMF Helper help file is accessible at:
# /Library/Application Support/JAMF/bin/jamfHelper.app/Contents/MacOS/jamfHelper -help
windowType="" # [hud | utility | fs]
windowPosition="" # [ul | ll | ur | lr]
title="" # "string"
heading="" # "string"
description="" # "string"
icon="" # path
iconSize="" # pixels
timeout="" # seconds
[ "$4" != "" ] && [ "$windowType" == "" ] && windowType=$4
[ "$5" != "" ] && [ "$windowPosition" == "" ] && windowPosition=$5
[ "$6" != "" ] && [ "$title" == "" ] && title=$6
[ "$7" != "" ] && [ "$heading" == "" ] && heading=$7
[ "$8" != "" ] && [ "$description" == "" ] && description=$8
[ "$9" != "" ] && [ "$icon" == "" ] && icon=$9
[ "$10" != "" ] && [ "$iconSize" == "" ] && iconSize=$10
[ "$11" != "" ] && [ "$timeout" == "" ] && timeout=$11
function setSnooze ()
{
## Get the time right now in Unix seconds
timeNow=$(date +"%s")
## Calculate the time for the next available display of the prompt (adds the time now and time chosen together in seconds)
timeNextRun=$((timeNow+SnoozeVal))
## Create or update a plist value containing the above next time to run value
/usr/bin/defaults write /Library/Preferences/com.acme.policy_001.snooze.plist DelayUntil -int $timeNextRun
exit 0
}
function showPrompt ()
{
## Prompt, and capture the output
HELPER=$("/Library/Application Support/JAMF/bin/jamfHelper.app/Contents/MacOS/jamfhelper" -windowType "$windowType" -windowPosition "$windowPosition" -title "$title" -heading "$heading" -description "$description" -icon "$icon" -iconSize "$iconSize" -button1 Install Now” -button2 Snooze -defaultButton 2 -cancelButton "2" -countdown "$timeout" -timeout "$timeout" -showDelayOptions "900, 1800, 3600, 7200, 14400")
echo "jamf helper result was $HELPER"
## Dissect the response to get just the button clicked and the value selected from the drop down menu
ButtonClicked="${HELPER: -1}"
SnoozeVal="${HELPER%?}"
echo "$ButtonClicked"
echo "$SnoozeVal"
if [ "$ButtonClicked" == "1" ]; then
echo "User chose Install"
/usr/bin/jamf policy -trigger office2019
exit 0
elif [ "$ButtonClicked" == "2" ]; then
echo "User chose Snooze"
setSnooze
fi
}
## Get a value (if possible) from a plist of the next valid time we can prompt the user
SnoozeValueSet=$(/usr/bin/defaults read /Library/Preferences/com.acme.policy_001.snooze.plist DelayUntil 2>/dev/null)
## If we got something from the plist...
if [ -n "$SnoozeValueSet" ]; then
## See what time it is now, and compare it to the value in the plist.
## If the time now is greater or equal to the value in the plist, enough time has elapsed, so...
timeNow=$(date +"%s")
if [[ "$timeNow" -ge "$SnoozeValueSet" ]]; then
## Display the prompt to the user again
showPrompt
else
## If the time now is less than the value in the plist, exit
echo "Not enough time has elapsed. Exiting..."
exit 0
fi
else
## If no value was in the plist or the plist wasn't there, assume it is the first run of the script and prompt them
showPrompt
fi
Some explanation:
First, the script checks to see if there was a previously stored value in seconds of when the next valid time is for the prompt to show up. This gets calculated by taking the value from the drop down and, if the user clicks the Snooze button, adds that onto the current time in (Unix) seconds, then stores that in a plist on the system. The script will check for this first to see if enough time has passed to even show the prompt again. If it has, it calls a function that shows the jamfHelper window.
Then, in these lines:
ButtonClicked="${HELPER: -1}"
SnoozeVal="${HELPER%?}"
It takes the smooshed output from jamfHelper and splits them into one value for the button and one for the drop down menu.
It then checks to see which button was clicked. If Install was clicked it just goes ahead and calls that other policy by the custom event trigger. If Snooze was clicked it updates or creates the plist with the updated value. Again, this is unix time now + the delay value in seconds. So for example, if you choose 30 minutes, that's 1800 seconds added onto the current time. That all gets done in this function:
function setSnooze ()
{
## Get the time right now in Unix seconds
timeNow=$(date +"%s")
## Calculate the time for the next available display of the prompt (adds the time now and time chosen together in seconds)
timeNextRun=$((timeNow+SnoozeVal))
## Create or update a plist value containing the above next time to run value
/usr/bin/defaults write /Library/Preferences/com.acme.policy_001.snooze.plist DelayUntil -int $timeNextRun
exit 0
}
So in your policy what you would, in addition to adding any script parameters to customize the message, is make sure it's running on each check in. If your check-in is 5 minutes, it means anywhere from 5 to about 10 minutes, the script runs, checks the previously stored value in the plist, and does some math. If the time it's running exceeds or equals the last value stored, then it shows the prompt again. If not, it just exits.
In this way, you can approximate showing the prompt again in the time chosen by the user. I say approximate only because it's actually impossible to get Jamf Pro to run a policy at very specific time. The check-in always adds in a random delay of anywhere from 0 to 300 seconds by default. So it may be that more than 15 or 30 minutes (or whatever they chose) elapses before they see the prompt again. But never less using the above method.
Hope that helps. Feel free to change the /Library/Preferences/com.acme.policy_001.snooze.plist
plist name in the script to something that makes sense to you. It shows up in 2 places in the script so be sure to update it in both.
Posted on 09-18-2019 06:15 AM
Wow, thanks @mm2270! I am going to play around with this shortly. I appreciate you taking the take to break this down for me, and other users who i am sure will benefit from it.
Posted on 03-15-2021 11:22 AM
Hello @mm2270 ,
I am circling back to this Jamf Helper script. :)
In trying to implement it now, I am finding that the "Snooze" button is not appearing, just the "Install" button. I was wondering if you had insight as to why this might be?
Thank you!
Posted on 03-15-2021 11:31 AM
Hey @dtmille2 Sure. I just took a look at the script I wrote back in 2019 above, and I see an error on this line:
HELPER=$("/Library/Application Support/JAMF/bin/jamfHelper.app/Contents/MacOS/jamfhelper" -windowType "$windowType" -windowPosition "$windowPosition" -title "$title" -heading "$heading" -description "$description" -icon "$icon" -iconSize "$iconSize" -button1 Install Now” -button2 Snooze -defaultButton 2 -cancelButton "2" -countdown "$timeout" -timeout "$timeout" -showDelayOptions "900, 1800, 3600, 7200, 14400")
The error is here:
-button1 Install Now” -button2 Snooze
I have a closing double quote mark after "Install Now", but not an opening one. And to boot, it somehow turned into a curly quote when I posted it, which definitely isn't going to work in a script. I needs to look like this instead:
-button1 "Install Now" -button2 Snooze
Make that edit and try it again and it should (I think) show both buttons.
Posted on 03-15-2021 11:54 AM
That was it @mm2270 , thank you!