With the goal of having better user interaction options that what is provided within Jamf's Patch Management Policies (not having the app quit 5 minutes later without warning) I have been testing different options with my existing update workflow and open source tools like Yo. I ended up using Alerter which will wait for user interaction within a script and allows the calling of a command with sudo permissions in order to execute a policy trigger.

I haven't deployed it to my fleet yet but figured I would see what feedback the community might have. It takes a 2 policies and 1 smart group per software title, which is a bit cumbersome, and currently never forces the update as the user could infinitely defer the installation if the app is never quit. I use a deferral counter for macOS software updates that could be integrated but I have not taken the time to do so yet.
I'm open to any and all feedback, thanks!
Setup Prior to Workflow
Script
By setting up the Script Parameters it can be adapted to any title
#!/bin/bash
# If app is open, alert user with the option to quit the app or defer for later. If user chooses to install it will quit the app, trigger the installation,
# then alert the user the policy is complete with the option to reopen the app. If the app is not open it will trigger the installation without alerting
# $4 = Title
# $5 = ParameterID
# $6 = Process Name
# $7 = Jamf Policy Event
# $8 = App Path
# Define currently logged in user
currentUser="$(ls -la /dev/console | cut -d " " -f 4)"
# Look if app is open via process name
appOpen="$(pgrep -ix "$6" | wc -l)"
if [[ $appOpen -gt 0 ]]; then
updateAnswer="$(/Library/Application Support/JAMF/alerter -title "$4" -sender $5 -message "must be quit in order to update. Save all data before quitting." -closeLabel Later -actions "Quit & Update")"
if [[ $updateAnswer == "Quit & Update" ]]; then
sudo -u $currentUser killall "$6"
jamf policy -event "$7"
reopenAnswer="$(/Library/Application Support/JAMF/alerter -title "$4" -sender "$5" -message "has been updated. Thank you." -closeLabel Ok -actions Reopen -timeout 60)"
if [[ $reopenAnswer == Reopen ]]; then
sudo -u $currentUser open "$8"
fi
else
echo "User deferred"
fi
else
jamf policy -event "$7"
fi
Policy
1. Create package that installs the alerter
executable to /Library/Application Support/JAMF/
2. Push pkg via policy to install Alerter pkg to All Manage Clients (or whatever scope will utilize this workflow)
Patch Management Title
Create a Patch Management Title for 'Google Chrome'. If the app you are looking to update is not available as a patch Management Title setup your smart group to have Application Version
instead of Patch Reporting
.
Testing Rings
To allow the update to be pushed out in rings for testing, pre-defined Static/Smart Groups are setup with the following logic:
Ring 1 - Smart Group based IT computers
Ring 2 - Static Group of manually selected group with various roles and use cases across organization, around 15-20% of computer count
Ring 3 - all computers not in Ring 1 or Ring 2
Workflow Setup
Smart Computer Group
Create a Smart Computer Group to scope to any computer with the app installed and not running the desired version
1) Smart Group name: *Google Chrome Smart Update
(Asterisk to keep Smart Update groups grouped at the top of your Smart Computer Group list)
2) Application Title
is
Google Chrome.app
3) Patch Reporting: Google Chrome
is not
65.0.3325.181
Policies
Create a Policy used to trigger the installation of the pkg
1) Policy name: Install Latest Google Chrome Trigger
2) Custom Trigger: install_googleChromeUpdate
3) Execution Frequency: Ongoing
4) Packages: GoogleChrome-65.0.3325.181.pkg
5) Scope: *Google Chrome Smart Update
6) Maintenance: Update Inventory
Create policy that will prompt user if app is open, install if it is not open
1) Policy Name: Install Latest Google Chrome Alerter
2) Scripts: Alerter App Updates
3) Parameter Values
a. Title: Google Chrome
b. Sender ID: com.google.chrome
c. Process Name: Google Chrome
d. Jamf Policy Event: install_googleChromeUpdate
e. App Path: /Applications/Google Chrome.app
4) Scope:
a. Target: *Google Chrome Smart Update
b. Exclusions: Ring 1
, Ring 2
, Ring 3
Ongoing Maintenance
When a new update is release the following maintenance would be required
1) Upload new pkg release for Google Chrome
2) Change Smart Computer Group the Patch Reporting version to new release number
3) Update the pkg within Install Latest Google Chrome Trigger
policy
4) Reset Ring Exclusions - Based on testing schedule remove a group from the exclusions as needed (start with Ring 2
and Ring 3
as excluded, one week later remove Ring 2
, one week after that remove Ring 3
)
@kendalljjohnson Thank you for this. I've been giving your script a try using Google Chrome and it seems to work great, but I'm running into an issue when I click on the "Reopen" button after Chrome is updated. Instead of relaunching Chrome, it is opening up the user's home folder in Finder. Any ideas why it is doing that? I copied your final version of the script and the only change I made was updating the path to the Alerter executable.
Disregard. I guess I needed to define the Open App Path. Thanks again for the script!
@kendalljjohnson this is excellent and very well thought out and thanks to those who have shared their mods. I can't believe i missed this! i'll be adding this to my workflow for sure and looking to make my own adjustments. this would be great if jamf could adopt something similar into their product
Can someone explain how the process is suppose to work for this? I tried setting up a test machine to test this out and all I got was a loop of a patch installing. How do you tell the scrip to popup the notification first? How can you test that? I am a bit confused on the workflow aspect.
There is a policy that triggers the install of the package and then the policy that triggers if the app is open. However... that doesn't seem to be working.
Hey @Kristopher,
There are 2 different policies:
- The Alerter policy
-This is scoped to a smart group that looks for all computers that have the software installed but not on the desired version
-If the user chooses to update and not defer, it calls on the other policy that installs the update via a custom policy trigger
- The actual updater policy
Here are an example of the 2 policies (in reverse order).

And what script variables I use for Google Chrome.

Hope that helps, happy to answer any questions you might have!
This is fantastic! Just what I needed for my OS updates. Can you tell me how to get rid of the little tiny terminal icon? 
@bergmire In that example, -appIcon
is set as the black/white circle logo and -sender
is not set so it provides the generic exec icon. If you were to use -sender "com.jamfsoftware.selfservice.mac"
in that same command, the App Icon would remain the same and the exec icon would now be the self service icon. You could not set -appIcon
and just use -sender
and there would be only 1 icon on the left.
Here are some examples that should help clarify:
alerter -title "Test Alert" -sender "com.jamfsoftware.selfservice.mac" -message "Do something." -closeLabel "Later" -actions "Ok"

alerter -title "Test Alert" -sender "com.jamfsoftware.selfservice.mac" -appIcon "/System/Library/CoreServices/Dock.app/Contents/Resources/dashboard.png" -message "Do something." -closeLabel "Later" -actions "Ok"

alerter -title "Test Alert" -appIcon "/System/Library/CoreServices/Dock.app/Contents/Resources/dashboard.png" -message "Do something." -closeLabel "Later" -actions "Ok"

Hope that helps!
Interesting scipts. Can this somehow be changed to prompts that appear central on the page, as those notifications user simply don“t take care about in my company, as so much information appear in these notifications
@kendalljjohnson Can you use Alerter to open a URL?
I can get it to work in terminal notifier but not Alerter...
@Captainamerica Not that I am aware of. It taps into notification center which is always in the top right corner. For alerts in the middle of the screen I would imaged JamfHelper is your best bet.
@myronjoffe Yes, this is definitely possible. The only trick is getting it to run as the logged in user, not the sudo user sent via Jamf. Here's a quick adaptation I did from an alert I was using. My original was used to alert users that Mojave was available in Self Service and if they wanted to go there the open button would launch Self Service. Here's a to open a website:
#!/bin/bash
################################DEFINE USER################################
currentUser=`python -c 'from SystemConfiguration import SCDynamicStoreCopyConsoleUser; import sys; username = (SCDynamicStoreCopyConsoleUser(None, None, None) or [None])[0]; username = [username,""][username in [u"loginwindow", None, u""]]; sys.stdout.write(username + "
");'`
# Identify the UID of the logged-in user
currentUserUID=`id -u "$currentUser"`
################################ALERTER FUNCTION################################
fAlerter ()
{
updateAnswer="$(/bin/launchctl asuser "$currentUserUID" /Library/Application Support/JAMF/alerter -title "Website" -sender "com.jamfsoftware.selfservice.mac" -message "Check out this website!" -closeLabel "Later" -actions "Open Now")"
if [[ "$updateAnswer" == "Open Now" ]]; then
sudo su - $currentUser -c "open https://www.jamf.com"
else
echo "Try again later"
fi
}
fAlerter &
Hello @kendalljjohnson thank you so much for your effort and time to help us out.
I am trying to understand the workflow so please excuse me if this has been hashed out and I simply missed it.
You have a policy that begins the update process and your larger script above will automatically launch the alerter portion if needed? If not needed, your script will begin the process. I see a your main script listed above but also your alerter script. I am trying to figure out if I need both. Thank you for your patience with me.
@mconners
The first policy has a script to check if the app that needs to be updated is running. If it is running, it will prompt for the user to defer or quit the app now to perform the update. If they choose now, it would trigger another policy that actually performs the install. If they defer, it changes the deferral count until the count runs out, so the only option is to update.
If the app is not running, it should call upon the trigger to have the install since the app is not in use.
Hopefully that makes sense, happy to chat through the process if not.
Thank you @kendalljjohnson, so in a sense, the second script is only for using as an alerter for other purposes. The script you gave us really is all self contained, triggering the actual policy to do the install.
@mconners
I guess I'm not sure which you mean by second script.
The script I marked as solved from 10/18/18 is the one I'm currently using for this process, replacing the original script posted when I first started this post.
Sorry @kendalljjohnson got it. Sorry, I am slightly off today.
A few posts up, you had a script for the alerter, posted on 2/5/19. That is throwing me off, but I think you intended this to be a stand alone item for working with the alerter function. Maybe I am totally off base and the way my day has gone, I am not surprised.
@mconners Ah, yes. That specific script is a quick stand-alone script that will prompt the user to either open the website or defer in response to a specific question about launching a website using Alerter.
Thank you @kendalljjohnson I should be good to go for testing. Thanks for your help, I sincerely appreciate it.
Hey @kendalljjohnson I hate to bother you again on this. Please pardon my interruption. I am new to the entire scripting thing and I am just now working through many different workflows where I will be using them often. This gives me a chance to learn how to use them and be more efficient in my processes.

I see in your example above, your script parameters in the policy are showing up as Title, App ID and so forth. When I load the script into my policy, I am not seeing this. Is there something special I need to do for the display to show up this way? All I am seeing is Parameter 1, Parameter 2 and so on.

Again, thank you for helping me understand this better.
@mconners No prob. In your Jamf Pro Settings, go to the script you uploaded and navigate to the "Options" section. That is where you can put in your own custom labels for the parameters 4-11 you use in the script.
@mconners You need to go back to the script itself, go into Edit mode, and then on the Options tab add custom labels for each parameter. Once you do that and save it, when you go back into your policy that uses that script it will show those custom labels for the parameters instead of the default ones.
Thank you both, @kendalljjohnson and @mm2270 that was almost too easy!! Sorry I didn't even look there, this is very helpful. Enjoy your afternoon's.
:(
Apple Seed 10.15 Alerter post
If anyone has some feedback would be much appreciated...
@myronjoffe From what I can tell, Alerter won't launch when calling it from a non-Apple BundleID. When you do call it from an Apple BundleID, for example com.apple.package, it will launch but just show the generic exec icon. Definitely not as visually appealing but will need to play and see what options we have.
Unfortunately, this is the downside of using a 3rd party tool like this (specially one that hasn't been touched in 4 years), Apple can easily cut the legs out on it at any time. Hoping to find a solution based on how much we utilize it!
@kendalljjohnson Thanks for the tip. I have also raised an issue with the dev on Github.
FWIW, I have built similar flows in the past and have a static set of Python code that does this. It leverages NSRunningApplication
to check if apps are running by Bundle ID, and quits or force quits them in the same fashion. I stopped using PIDs and things like pgrep
due to false positives from things like helpers and user agents, example below with Safari:
bash-3.2$ pgrep Safari
1017
5181
5192
5193
13812
bash-3.2$ ps -ef | grep Safari
502 1017 1 0 Fri09AM ?? 0:18.25 /System/Library/CoreServices/SafariSupport.bundle/Contents/MacOS/SafariBookmarksSyncAgent
502 5181 1 0 Fri03PM ?? 0:00.17 /System/Library/PrivateFrameworks/SafariShared.framework/Versions/A/XPCServices/com.apple.Safari.History.xpc/Contents/MacOS/com.apple.Safari.History
502 5192 1 0 Fri03PM ?? 0:00.24 /System/Library/Frameworks/SafariServices.framework/Versions/A/XPCServices/com.apple.SafariServices.ExtensionHelper.xpc/Contents/MacOS/com.apple.SafariServices.ExtensionHelper
502 5193 1 0 Fri03PM ?? 0:24.93 /System/Library/PrivateFrameworks/SafariSafeBrowsing.framework/com.apple.Safari.SafeBrowsing.Service
502 11307 603 0 11:50AM ?? 17:26.88 /Applications/Slack.app/Contents/Frameworks/Slack Helper.app/Contents/MacOS/Slack Helper --type=renderer --autoplay-policy=no-user-gesture-required --force-color-profile=srgb --enable-features=SharedArrayBuffer --disable-features=MacV2Sandbox --service-pipe-token=4643921532856445533 --lang=en-US --standard-schemes=slack-resources,slack-sounds,slack-webapp-dev --secure-schemes=slack-resources,slack-sounds,slack-webapp-dev --app-path=/Applications/Slack.app/Contents/Resources/app.asar --user-agent=Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Slack/4.0.2 Chrome/69.0.3497.128 Electron/4.2.8 Safari/537.36 Sonic Slack_SSB/4.0.2 --node-integration=false --webview-tag=false --no-sandbox --preload=/Applications/Slack.app/Contents/Resources/app.asar/dist/ssb-interop.bundle.js --background-color=#FFFFFF --num-raster-threads=4 --enable-zero-copy --enable-gpu-memory-buffer-compositor-resources --enable-main-frame-before-activation --service-request-channel-token=4643921532856445533 --renderer-client-id=9 {"preloadEnvironment":{"resourcePath":"/Applications/Slack.app/Contents/Resources/app.asar","isSonic":true,"appVersion":"4.0.2","teamId":"app","windowType":"main"},"crashReporterOpts":{"productName":"Slack","companyName":"Slack Technologies","uploadToServer":true,"submitURL":"https://slack.com/apps/breakpad?instanceUid=5f45913c-5ad7-51ff-9f92-3d6112838729","extra":{"instanceUid":"5f45913c-5ad7-51ff-9f92-3d6112838729","sessionId":"NWY0NTkxM2MtNWFkNy01MWZmLTlmOTItM2Q2MTEyODM4NzI5XzE1NjgzOTI0MDkyMDQ="}},"perfTimer":{"mainPid":603,"BOOT":[28,892285882],"numTeamsAtLaunch":3,"SHELL":[30,106792404],"APP_READY":[30,185617956],"APP_CREATED":[30,496675498],"MAIN_WINDOW_CREATING":[30,529137517]},"identifier":"slack_preload_metadata_arguments"}
502 13812 1 0 3:32PM ?? 0:00.08 /System/Library/Frameworks/SafariServices.framework/Versions/A/XPCServices/com.apple.SafariServices.xpc/Contents/MacOS/com.apple.SafariServices
502 19396 7741 0 9:50AM ttys000 0:00.00 grep Safari
Note, I do not have Safari running at all, and Slack also lists Mozilla as a user agent, so it would give you false positives on Firefox, and I just basically bolted messaging on top of jamf helper, example:


Auto Update Repo is where my code is stored, maybe it will be useful to some of you.