Custom Patch Management Workflow

kendalljjohnson
Contributor II

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.

28a985a9c48144409183df00b389e93b

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)

1 ACCEPTED SOLUTION

kendalljjohnson
Contributor II

Realized I never posted what I finally settled on and put in production in case others want to give it a try. Been running with it for a while and seems to be working pretty well! Thanks for all the input and testing from others in the thread.

#!/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
# Quit and Open path have 2 entries for the times you are quiting/uninstalling an old version of an app that is replaced by a new name (for example quiting Adobe Acrobat Pro, which is replaced by Adobe Acorbat.app)

################################DEFINE VARIABLES################################

# $4 = Title
# $5 = App ID
# $6 = Process Name
# $7 = Jamf Policy Event
# $8 = Quit App Path
# $9 = Open App Path

#Defining the Sender ID as self service due to setting the Sender ID as the actual app being updated would often cause the app to crash
sender="com.jamfsoftware.selfservice.mac"
#Jamf parameters can't be passed into a function, redefining the app path to be used within the funciton
quitPath="$8"
openPath="$9"

################################SETUP FUNCTIONS TO CALL################################

fGetCurrenUser (){
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"`
}

fQuitApp (){
cat > /private/tmp/quit_application.sh <<EOF
#!/bin/bash

/bin/launchctl asuser "$currentUserUID" /usr/bin/osascript -e 'tell application "$quitPath" to quit'
EOF

/bin/chmod +x /private/tmp/quit_application.sh
/bin/launchctl asuser "$currentUserUID" sudo -iu "$currentUser" "/private/tmp/quit_application.sh"
/bin/rm -f "/private/tmp/quit_application.sh"
}

fOpenApp (){
  cat > /private/tmp/open_application.sh <<EOF
#!/bin/bash

/usr/bin/open "$openPath"
EOF

/bin/chmod +x /private/tmp/open_application.sh
/bin/launchctl asuser "$currentUserUID" sudo -iu "$currentUser" "/private/tmp/open_application.sh"
/bin/rm -f "/private/tmp/open_application.sh"
}

################################SETUP TIMER FILE################################

## Set up the software update time if it does not exist already
if [ ! -e /Library/Application Support/JAMF/.$5.timer.txt ]; then
  echo "2" > /Library/Application Support/JAMF/.$5.timer.txt
fi

## Get the timer value
timer=`cat /Library/Application Support/JAMF/.$5.timer.txt`

################################ALERTER MESSAGE OPTIONS################################

saveQuitMSG="must be quit in order to update. Save all data before quitting."
updatedMSG="has been updated. Thank you."

################################START 'UPDATE WITH ALERTER' PROCESS################################

# Look if app is open via process name
appOpen="$(pgrep -ix "$6" | wc -l)"

# if the app is open and the defer timer is not zero
if [[ $appOpen -gt 0 && $timer -gt 0 ]]; then
    fGetCurrenUser
    updateAnswer="$(/bin/launchctl asuser "$currentUserUID" /Library/Application Support/JAMF/alerter -title "$4" -sender "$sender" -message "$saveQuitMSG" -closeLabel "Defer ($timer)" -actions "Quit & Update" -timeout 3600)"
    if [[ $updateAnswer == "Quit & Update" ]]; then
        #quit app, install the update, then prompt the user when complete and ask if they want to reopen the app. Message will time out after 60 secs.
        fQuitApp
        /usr/local/bin/jamf policy -event "$7"
        reopenAnswer="$(/bin/launchctl asuser "$currentUserUID" /Library/Application Support/JAMF/alerter -title "$4" -sender "$sender" -message "$updatedMSG" -closeLabel Ok -actions Reopen -timeout 60)"
        if [[ $reopenAnswer == Reopen ]]; then
            fOpenApp
        fi
        #reset timer after updating
        echo "2" > /Library/Application Support/JAMF/.$5.timer.txt

    else
        let CurrTimer=$timer-1
        echo "User chose to defer"
        echo "$CurrTimer" > /Library/Application Support/JAMF/.$5.timer.txt
        echo "Defer count is now $CurrTimer"
        exit 0
    fi
# if app is open and defer timer has run out
elif [[ $appOpen -gt 0 && $timer == 0 ]]; then
    fGetCurrenUser
    /bin/launchctl asuser "$currentUserUID" /Library/Application Support/JAMF/alerter -title "$4" -sender "$sender" -message "$saveQuitMSG" -actions "Quit & Update" -closeLabel "No Deferrals Left " -timeout 3600
    fQuitApp
    /usr/local/bin/jamf policy -event "$7"
    reopenAnswer="$(/bin/launchctl asuser "$currentUserUID" /Library/Application Support/JAMF/alerter -title "$4" -sender "$sender" -message "$updatedMSG" -closeLabel Ok -actions Reopen -timeout 60)"
    if [[ $reopenAnswer == Reopen ]]; then
        fOpenApp
    fi
    #reset timer after updating
    echo "2" > /Library/Application Support/JAMF/.$5.timer.txt

else
    # app is not open, reset timer and run updates
    echo "2" > /Library/Application Support/JAMF/.$5.timer.txt
    /usr/local/bin/jamf policy -event "$7"
fi

View solution in original post

112 REPLIES 112

Captainamerica
Contributor II

hmm - it still just shows the same picture(see in botom). Script looks like below. It is been working probably before is it because some kind of files is some where hidden that it act like this and does not show defer option ?

Script I execute is this

#!/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
# Quit and Open path have 2 entries for the times you are quiting/uninstalling an old version of an app that is replaced by a new name (for example quiting Adobe Acrobat Pro, which is replaced by Adobe Acorbat.app)

################################DEFINE VARIABLES################################

title="Google Chrome"
appID="com.google.chrome"
process="Google Chrome"
policy="install_googleChromeUpdate"
openpath1="/Applications/Google Chrome.app"
quitpath1="/Applications/Google Chrome.app"

#Defining the Sender ID as self service due to setting the Sender ID as the actual app being updated would often cause the app to crash
sender="com.jamfsoftware.selfservice.mac"
#Jamf parameters can't be passed into a function, redefining the app path to be used within the funciton
quitPath="$8"
openPath="$9"

################################SETUP FUNCTIONS TO CALL################################

fGetCurrenUser (){
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"`
}

fQuitApp (){
cat > /private/tmp/quit_application.sh <<EOF
#!/bin/bash

/bin/launchctl asuser "$currentUserUID" /usr/bin/osascript -e 'tell application "$quitPath" to quit'
EOF

/bin/chmod +x /private/tmp/quit_application.sh
/bin/launchctl asuser "$currentUserUID" sudo -iu "$currentUser" "/private/tmp/quit_application.sh"
/bin/rm -f "/private/tmp/quit_application.sh"
}

fOpenApp (){
    cat > /private/tmp/open_application.sh <<EOF
#!/bin/bash

/usr/bin/open "$openPath"
EOF

/bin/chmod +x /private/tmp/open_application.sh
/bin/launchctl asuser "$currentUserUID" sudo -iu "$currentUser" "/private/tmp/open_application.sh"
/bin/rm -f "/private/tmp/open_application.sh"
}

################################SETUP TIMER FILE################################

## Set up the software update time if it does not exist already
if [ ! -e /Library/Application Support/JAMF/.$5.timer.txt ]; then
    echo "2" > /Library/Application Support/JAMF/.$5.timer.txt
fi

## Get the timer value
timer=`cat /Library/Application Support/JAMF/.$5.timer.txt`

################################ALERTER MESSAGE OPTIONS################################

saveQuitMSG="must be quit in order to update. Save all data before quitting."
updatedMSG="has been updated. Thank you."

################################START 'UPDATE WITH ALERTER' PROCESS################################

# Look if app is open via process name
appOpen="$(pgrep -ix "$6" | wc -l)"

# if the app is open and the defer timer is not zero
if [[ $appOpen -gt 0 && $timer -gt 0 ]]; then
        fGetCurrenUser
        updateAnswer="$(/bin/launchctl asuser "$currentUserUID" /Library/Application Support/JAMF/alerter -title "$4" -sender "$sender" -message "$saveQuitMSG" -closeLabel "Defer ($timer)" -actions "Quit & Update" -timeout 3600)"
        if [[ $updateAnswer == "Quit & Update" ]]; then
                #quit app, install the update, then prompt the user when complete and ask if they want to reopen the app. Message will time out after 60 secs.
                fQuitApp
                /usr/local/bin/jamf policy -event "$7"
                reopenAnswer="$(/bin/launchctl asuser "$currentUserUID" /Library/Application Support/JAMF/alerter -title "$4" -sender "$sender" -message "$updatedMSG" -closeLabel Ok -actions Reopen -timeout 60)"
                if [[ $reopenAnswer == Reopen ]]; then
                        fOpenApp
                fi
                #reset timer after updating
                echo "2" > /Library/Application Support/JAMF/.$5.timer.txt

        else
                let CurrTimer=$timer-1
                echo "User chose to defer"
                echo "$CurrTimer" > /Library/Application Support/JAMF/.$5.timer.txt
                echo "Defer count is now $CurrTimer"
                exit 0
        fi
# if app is open and defer timer has run out
elif [[ $appOpen -gt 0 && $timer == 0 ]]; then
        fGetCurrenUser
        /bin/launchctl asuser "$currentUserUID" /Library/Application Support/JAMF/alerter -title "$4" -sender "$sender" -message "$saveQuitMSG" -actions "Quit & Update" -closeLabel "No Deferrals Left " -timeout 3600
        fQuitApp
        /usr/local/bin/jamf policy -event "$7"
        reopenAnswer="$(/bin/launchctl asuser "$currentUserUID" /Library/Application Support/JAMF/alerter -title "$4" -sender "$sender" -message "$updatedMSG" -closeLabel Ok -actions Reopen -timeout 60)"
        if [[ $reopenAnswer == Reopen ]]; then
                fOpenApp
        fi
        #reset timer after updating
        echo "2" > /Library/Application Support/JAMF/.$5.timer.txt

else
        # app is not open, reset timer and run updates
        echo "2" > /Library/Application Support/JAMF/.$5.timer.txt
        /usr/local/bin/jamf policy -event "$7"
fi

49c5896496bf4012abc1a20d45167b05

Rikky
New Contributor II

What version of Alerter are you using? I haven't upgraded yet. Version 2 seems to work for me.

Captainamerica
Contributor II

have downloaded the latest with catalina support and also worked before. I guess there must be somekind of trigger that causes this message to show instead of the defer option

jameson
Contributor II

Anyone got this working in Big Sur ?. I see the same issue as @Captainamerica I see the notification, but there is not any defer or install button that is seen ?

jameson
Contributor II

Update: It seems the button are now in the "option" dropdown, but the option button does not appear before hovering on it - not to smart. but I just want the buttons to be shown right away as it used to work ?!
f87dfdb73db647279f22bed026037cee

kendalljjohnson
Contributor II

@jameson Looks like is an Apple notification change, not Alerter. The same thing applies for certain native Apple notifications, but not ones like AirDrop receiving.

15ab940672cd4079b6db93662f4e6472

4dda6c38a3864744b8af83b247403fb1

One project I haven't taken the time to test out but might have different results would be Notifier by dataJAR. Biggest difference I've seen so far is if you want custom logos you would have to modify the Xcode project and re-sign it since it is an App.

jameson
Contributor II

The strange thing is that the alerter notifications does not stay active on the screen but dissapear again. Before Big Sur they stayed active until user did something. If that just could work it would be usable

kendalljjohnson
Contributor II

@jameson How long until they disappear for you? I'm not having that experience, or at least yet. It's been about 20min and my test notification is still there while I continue to work.

jameson
Contributor II

It just stay 2-3 seconds and then dissapear. Can you try and show the script you are using, so I can compare. I am on Big sur BTW.

EDIT: also tried to test on catalina 10.15.7 - but then just got an message that "alerter will damage the computer". even the file is not in qurantine. So if it works for you which version of alerter do you use ? - I have downloaded the latest on the github that should support big sur

kendalljjohnson
Contributor II

@jameson I haven't started deploying the newest version of Alerter, but I am running Big Sur on my computer with Alerter working without issue and a lot of our fleet is on 10.15.7 and I haven't heard or noticed any issues.

Here's what I use in my script, with variables being either defined within the policy script options or in a different section of the script:

/bin/launchctl asuser "$currentUserUID" /Library/Application Support/JAMF/alerter -title "$4" -sender "$sender" -message "$saveQuitMSG" -closeLabel "Defer ($timer)" -actions "Quit & Update" -timeout 3600)

jameson
Contributor II

Thanks - can you try and paste the hole script. Do you have any PPPC etc made ?

EDIT: I got this working now. Thanks

kendalljjohnson
Contributor II

This should still be accurate, with the only difference I can think of is I now use com.apple.AppStore as the Sender ID for all alerts. I wonder if that could be part of your issue? I know at one point I was having crashing errors if I used the actual app ID of the app I was trying to update so I switched to have all use the App Store assuming that it probably wasn't open and would provide an App Icon with the impression of a legit app update being requested.

jameson
Contributor II

I got it working with the new big sur release and did remove the quarantine flag on the file. But you write that you still use the old alerter release (and not the one that was released 3 weeks ago). But the old one will not work on big sur and is catched as malware, for me. So that wonders me you can get this working, unless of course you have disabled something on the build in spam catcher (if that is even possible to do)

kendalljjohnson
Contributor II

Not doing anything special besides deploying it via a pkg that puts in in the Jamf Application Support folder, not sure what would cause the difference in behavior.

jameson
Contributor II

Anyone still using this on big sur ?. is A PPPC needed so the alerter notification will be showed ? Actually have a strange issue, where on one big sur client the notification from alerter appear, while on another mac also on big sur the message does not appear

kendalljjohnson
Contributor II

Just last week my computer started flagging Alerter for malware but I have not seen any other reports in our fleet seeing the same behavior. Even tried grabbing the more recent release and it is flagged as well. I have had it included in our Notifications payload since the beginning so there shouldn't be any connection between approving the notifications and it getting flagged.

Due to this issue, I have rewritten the process to use JamfHelper as it will be a more reliable method going forward. Definitely not the same interface I would prefer but it seems the most future proof method at this time. I haven't deployed it yet or done all my testing with it but seems to be keep the desired behavior.

The basic command structure I'm using in the script is this, note I have added a new Script variable for App Icon Path that calls on the .icns file within each app's App Bundle:

/Library/Application Support/JAMF/bin/jamfHelper.app/Contents/MacOS/jamfHelper -windowType utility -windowPosition ur -icon "$iconPath" -iconSize 50 -heading "Update Available" -description "$4 must be quit in order to update. Save all data before quitting." -button1 "Quit & Update" -button2 "Defer ($timer)" -timeout 3600

da8178023b8242a0bb9f3358860a6839

jameson
Contributor II

OK. What I think is really strange that I use the exact same policy and on one big sur it works as expected and other not?. Have you tried it on M1 mac´s, as it is actually on M1 I see the issue where the notification never show up

jameson
Contributor II

@kendalljjohnson

I have tried to change to jamf helper - but seems something is wrong

#!/bin/sh
#!/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
# Quit and Open path have 2 entries for the times you are quiting/uninstalling an old version of an app that is replaced by a new name (for example quiting Adobe Acrobat Pro, which is replaced by Adobe Acorbat.app)

################################DEFINE VARIABLES################################

#$4 = Title
#$5 = App ID
#$6 = Process Name
#$7 = Jamf Policy Event
#$8 = Quit App Path
#$9 = Open App Path



quitPath="$8"
openPath="$9"

################################SETUP FUNCTIONS TO CALL################################

fGetCurrenUser (){
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"`
}

fQuitApp (){
cat > /private/tmp/quit_application.sh <<EOF
#!/bin/bash

/bin/launchctl asuser "$currentUserUID" /usr/bin/osascript -e 'tell application "$quitPath" to quit'
EOF

/bin/chmod +x /private/tmp/quit_application.sh
/bin/launchctl asuser "$currentUserUID" sudo -iu "$currentUser" "/private/tmp/quit_application.sh"
/bin/rm -f "/private/tmp/quit_application.sh"
}

fOpenApp (){
  cat > /private/tmp/open_application.sh <<EOF
#!/bin/bash

/usr/bin/open "$openPath"
EOF

/bin/chmod +x /private/tmp/open_application.sh
/bin/launchctl asuser "$currentUserUID" sudo -iu "$currentUser" "/private/tmp/open_application.sh"
/bin/rm -f "/private/tmp/open_application.sh"
}

################################SETUP TIMER FILE################################

## Set up the software update time if it does not exist already
if [ ! -e /Library/Application Support/JAMF/.$5.timer.txt ]; then
  echo "2" > /Library/Application Support/JAMF/.$5.timer.txt
fi

## Get the timer value
timer=$(cat /Library/Application Support/JAMF/.$5.timer.txt)

################################ALERTER MESSAGE OPTIONS################################


################################START 'UPDATE WITH ALERTER' PROCESS################################

# Look if app is open via process name
appOpen=$(pgrep -ix "Google Chrome" | wc -l)

# if the app is open and the defer timer is not zero
if [[ $appOpen -gt 0 && $timer -gt 0 ]]; then
    fGetCurrenUser
    updateAnswer="$(/bin/launchctl asuser "$currentUserUID" /Library/Application Support/JAMF/bin/jamfHelper.app/Contents/MacOS/jamfHelper -windowType utility -windowPosition ur -icon "$iconPath" -iconSize 50 -heading "Update Available" -description "Micorosoft Outlook must be quit in order to update. Save all data before quitting." -button1 "Quit & Update" -button2 "Defer ($timer)" -timeout 3600)

    if [[ $updateAnswer == "Quit & Update" ]]; then
        #quit app, install the update, then prompt the user when complete and ask if they want to reopen the app. Message will time out after 60 secs.
        fQuitApp
        /usr/local/bin/jamf policy -event "$7"
        reopenAnswer="$(/bin/launchctl asuser "$currentUserUID" /Library/Application Support/JAMF/bin/jamfHelper.app/Contents/MacOS/jamfHelper -windowType utility -windowPosition ur -icon "$iconPath" -iconSize 50 -heading "Update Available" -description "Micorosoft Outlook must be quit in order to update. Save all data before quitting." -button1 "Quit & Update" -button2 "Defer ($timer)" -timeout 3600)
        if [[ $reopenAnswer == Reopen ]]; then
            fOpenApp
        fi
        #reset timer after updating
        echo "2" > /Library/Application Support/JAMF/.$5.timer.txt

    else
        let CurrTimer=$timer-1
        echo "User chose to defer"
        echo "$CurrTimer" > /Library/Application Support/JAMF/.$5.timer.txt
        echo "Defer count is now $CurrTimer"
        exit 0
    fi
# if app is open and defer timer has run out
elif [[ $appOpen -gt 0 && $timer == 0 ]]; then
    fGetCurrenUser
    /bin/launchctl asuser "$currentUserUID" /Library/Application Support/JAMF/bin/jamfHelper.app/Contents/MacOS/jamfHelper -windowType utility -windowPosition ur -icon "$iconPath" -iconSize 50 -heading "Update Available" -description "Micorosoft Outlook must be quit order to update. Save all data before quitting" -button1 "Quit & Update" -button2 "Defer ($timer)" -timeout 3600)

    fQuitApp
    /usr/local/bin/jamf policy -event "$7"
    reopenAnswer="$(/bin/launchctl asuser "$currentUserUID" /Library/Application Support/JAMF/bin/jamfHelper.app/Contents/MacOS/jamfHelper -windowType utility -windowPosition ur -icon "$iconPath" -iconSize 50 -heading "Update Available" -description "Micorosoft Outlook must be quit in order to update. Save all data before quitting." -button1 "Quit & Update" -button2 "Defer ($timer)" -timeout 3600)

    if [[ $reopenAnswer == Reopen ]]; then
        fOpenApp
    fi
    #reset timer after updating
    echo "2" > /Library/Application Support/JAMF/.$5.timer.txt

else
    # app is not open, reset timer and run updates
    echo "2" > /Library/Application Support/JAMF/.$5.timer.txt
    /usr/local/bin/jamf policy -event "$7"
fi

kendalljjohnson
Contributor II

You would also have to update the if statements since JamfHelper's output is different than Alerter and I don't think it hurts having it but JamfHelper doesn't require the launchctl asuser aspect. Here's my full script that I've been playing with so far:

#!/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

################################DEFINE VARIABLES################################

# $4 = Title
# $5 = App ID
# $6 = Process Name
# $7 = App Icon Path
# $8 = Jamf Policy Event
# $9 = Quit App Path
# $10 = Open App Path

#Jamf parameters can't be passed into a function, redefining the app path to be used within the funciton
iconPath="$7"
quitPath="$9"
openPath="$10"

################################SETUP FUNCTIONS TO CALL################################

fGetCurrenUser (){
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"`
}

fCheckAppIconPath (){
#Check if iconPath is still valid, use PLNU logo if not found
if [ -f "$iconPath" ]; then
    echo "App Icon Path still valid"
else
    echo "App Icon not found, use generic logo"
    iconPath="/Applications/App Store.app/Contents/Resources/AppIcon.icns"
fi
}

fQuitApp (){
cat > /private/tmp/quit_application.sh <<EOF
#!/bin/bash

/bin/launchctl asuser "$currentUserUID" /usr/bin/osascript -e 'tell application "$quitPath" to quit'
EOF

/bin/chmod +x /private/tmp/quit_application.sh
/bin/launchctl asuser "$currentUserUID" sudo -iu "$currentUser" "/private/tmp/quit_application.sh"
/bin/rm -f "/private/tmp/quit_application.sh"
}

fOpenApp (){
  cat > /private/tmp/open_application.sh <<EOF
#!/bin/bash

/usr/bin/open "$openPath"
EOF

/bin/chmod +x /private/tmp/open_application.sh
/bin/launchctl asuser "$currentUserUID" sudo -iu "$currentUser" "/private/tmp/open_application.sh"
/bin/rm -f "/private/tmp/open_application.sh"
}

################################SETUP TIMER FILE################################

## Set up the software update time if it does not exist already
if [ ! -e /Library/Application Support/JAMF/.$5.timer ]; then
  echo "2" > /Library/Application Support/JAMF/.$5.timer
fi

## Get the timer value
timer=`cat /Library/Application Support/JAMF/.$5.timer`

################################ALERTER MESSAGE OPTIONS################################

saveQuitMSG="must be quit in order to update. Save all data before quitting."
updatedMSG="has been updated. Thank you."

################################START 'UPDATE WITH ALERTER' PROCESS################################

# Look if app is open via process name
appOpen="$(pgrep -ix "$6" | wc -l)"


# if the app is open and the defer timer is not zero
if [[ $appOpen -gt 0 && $timer -gt 0 ]]; then
    fGetCurrenUser
    fCheckAppIconPath
    updateAnswer="$(/Library/Application Support/JAMF/bin/jamfHelper.app/Contents/MacOS/jamfHelper -windowType utility -windowPosition ur -icon "$iconPath" -iconSize 50 -heading "Update Available" -description "$4 must be quit in order to update. Save all data before quitting." -button1 "Quit & Update" -button2 "Defer ($timer)" -timeout 3600)"
    if [[ $updateAnswer == "0" ]]; then
        #quit app, install the update, then prompt the user when complete and ask if they want to reopen the app. Message will time out after 60 secs.
        fQuitApp
        /usr/local/bin/jamf policy -event "$8"
        reopenAnswer="$(/Library/Application Support/JAMF/bin/jamfHelper.app/Contents/MacOS/jamfHelper -windowType utility -windowPosition ur -icon "$iconPath" -iconSize 50 -description "$4 has been updated" -button1 "Reopen" -button2 "Ok" -timeout 60)"
        if [[ $reopenAnswer == "0" ]]; then
            fOpenApp
        fi
        #reset timer after updating
        echo "2" > /Library/Application Support/JAMF/.$5.timer

    else
        let CurrTimer=$timer-1
        echo "User chose to defer"
        echo "$CurrTimer" > /Library/Application Support/JAMF/.$5.timer
        echo "Defer count is now $CurrTimer"
        exit 0
    fi
# if app is open and defer timer has run out
elif [[ $appOpen -gt 0 && $timer == 0 ]]; then
    fGetCurrenUser
    fCheckAppIconPath
    /Library/Application Support/JAMF/bin/jamfHelper.app/Contents/MacOS/jamfHelper -windowType utility -windowPosition ur -icon "$iconPath" -iconSize 50 -heading "Update Available" -description "$4 must be quit in order to update. Save all data before quitting." -button1 "Quit & Update" -button2 "No Deferrals Left" -timeout 3600
    fQuitApp
    /usr/local/bin/jamf policy -event "$8"
    reopenAnswer="$(/Library/Application Support/JAMF/bin/jamfHelper.app/Contents/MacOS/jamfHelper -windowType utility -windowPosition ur -icon "$iconPath" -iconSize 50 -description "$4 has been updated" -button1 "Reopen" -button2 "Ok" -timeout 60)"
    if [[ $reopenAnswer == "0" ]]; then
        fOpenApp
    fi
    #reset timer after updating
    echo "2" > /Library/Application Support/JAMF/.$5.timer

else
    # app is not open, reset timer and run updates
    echo "2" > /Library/Application Support/JAMF/.$5.timer
    /usr/local/bin/jamf policy -event "$8"
fi

jameson
Contributor II

Great thanks - I will give it a go

Not applicable

Thanks a lot

kendalljjohnson
Contributor II

Just discovered one issue with the script above when performing the reopen process: the $10 parameter needs to be ${10}. It was interpreting $1 and then a 0, so it it became /0.

Edit line 20 to be:

openPath="${10}"

myronjoffe
Contributor III

this looks pretty slick mac@ibm-notification-tool

kendalljjohnson
Contributor II

Whoa, nice. Playing with it a little and it seems like the notification appears and then hides pretty quickly where our goal would be to have the notification stay on screen. The -timeout option only applies to the Pop-Up option and not the notification banner. I'll definitely keep an eye on this project to see where it goes, thanks for the reference!

myronjoffe
Contributor III

cool

macsimus
New Contributor II

Has anyone played around with making this script be able to close multiple apps? For example for Acrobat Reader update you need Reader and Safari to be closed I believe.

Captainamerica
Contributor II

Funny - I am having the exact same issue. I guess the key is this one, if there can be build in more applications

Look if app is open via process name

appOpen="$(pgrep -ix "$6" | wc -l)"

mottertektura
Contributor

Anyone else running into this notarization issue and have a fix? I tried creating a new package for version .004 of alerter and signing the package with our Developer ID and it's still throwing this prompt on 11.4 😞 Thanks!

Screen Shot 2021-07-14 at 6.38.47 PM.png

tender
New Contributor III

Hi @kendalljjohnson this looks very useful, going to test it out. Can you post the most recent version of the script you are using with JAMF Helper?

nice work!

tend·er (tĕn′dər) noun: One who tends something.

Yeah @tender, no problem. Good opportunity to make sure my comments are up to date!

#!/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

################################DEFINE VARIABLES################################

# $4 = Title
# Example: "Google Chrome"
# $5 = App ID
# Example: "com.google.chrome"
# $6 = Process Name
# Example: "Google Chrome"
# $7 = App Icon Path
# Example: "/Applications/Google Chrome.app/Contents/Resources/app.icns"
# $8 = Jamf Policy Event
# Example: "install_googleChromeUpdate"
# $9 = Quit App Path
# Example: "/Applications/Google Chrome.app"
# $10 = Open App Path
# Example: "/Applications/Google Chrome.app"

#Jamf parameters can't be passed into a function, redefining the app path to be used within the funciton
iconPath="$7"
quitPath="$9"
openPath="${10}"

################################SETUP FUNCTIONS TO CALL################################

fGetCurrenUser (){
currentUser=`/bin/ls -l /dev/console | /usr/bin/awk '{ print $3 }'`

  # Identify the UID of the logged-in user
  currentUserUID=`id -u "$currentUser"`
}

fCheckAppIconPath (){
#Check if iconPath is still valid, use App Store logo if not found
if [ -f "$iconPath" ]; then
    echo "App Icon Path still valid"
else
    echo "App Icon not found, use generic logo"
    iconPath="/Applications/App Store.app/Contents/Resources/AppIcon.icns"
fi
}

fQuitApp (){
cat > /private/tmp/quit_application.sh <<EOF
#!/bin/bash

/bin/launchctl asuser "$currentUserUID" /usr/bin/osascript -e 'tell application "$quitPath" to quit'
EOF

/bin/chmod +x /private/tmp/quit_application.sh
/bin/launchctl asuser "$currentUserUID" sudo -iu "$currentUser" "/private/tmp/quit_application.sh"
/bin/rm -f "/private/tmp/quit_application.sh"
}

fOpenApp (){
  cat > /private/tmp/open_application.sh <<EOF
#!/bin/bash

/usr/bin/open "$openPath"
EOF

/bin/chmod +x /private/tmp/open_application.sh
/bin/launchctl asuser "$currentUserUID" sudo -iu "$currentUser" "/private/tmp/open_application.sh"
/bin/rm -f "/private/tmp/open_application.sh"
}

################################SETUP TIMER FILE################################

## Set up the software update time if it does not exist already
if [ ! -e /Library/Application\ Support/JAMF/.$5.timer ]; then
  echo "2" > /Library/Application\ Support/JAMF/.$5.timer
fi

## Get the timer value
timer=`cat /Library/Application\ Support/JAMF/.$5.timer`

################################ALERT MESSAGE OPTIONS################################

saveQuitMSG="must be quit in order to update. Save all data before quitting."
updatedMSG="has been updated. Thank you."

################################START 'UPDATE WITH JAMFHELPER' PROCESS################################

# Look if app is open via process name
appOpen="$(pgrep -ix "$6" | wc -l)"


# if the app is open and the defer timer is not zero
if [[ $appOpen -gt 0 && $timer -gt 0 ]]; then
    fGetCurrenUser
    fCheckAppIconPath
    updateAnswer="$(/Library/Application\ Support/JAMF/bin/jamfHelper.app/Contents/MacOS/jamfHelper -windowType utility -windowPosition ur -icon "$iconPath" -iconSize 50 -heading "Update Available" -description "$4 must be quit in order to update. Save all data before quitting." -button1 "Quit & Update" -button2 "Defer ($timer)" -timeout 3600)"
    if [[ $updateAnswer == "0" ]]; then
        #quit app, install the update, then prompt the user when complete and ask if they want to reopen the app. Message will time out after 60 secs.
        fQuitApp
        /usr/local/bin/jamf policy -event "$8"
        reopenAnswer="$(/Library/Application\ Support/JAMF/bin/jamfHelper.app/Contents/MacOS/jamfHelper -windowType utility -windowPosition ur -icon "$iconPath" -iconSize 50 -description "$4 has been updated" -button1 "Reopen" -button2 "Ok" -timeout 60)"
        if [[ $reopenAnswer == "0" ]]; then
            fOpenApp
        fi
        #reset timer after updating
        echo "2" > /Library/Application\ Support/JAMF/.$5.timer

    else
        let CurrTimer=$timer-1
        echo "User chose to defer"
        echo "$CurrTimer" > /Library/Application\ Support/JAMF/.$5.timer
        echo "Defer count is now $CurrTimer"
        exit 0
    fi
# if app is open and defer timer has run out
elif [[ $appOpen -gt 0 && $timer == 0 ]]; then
    fGetCurrenUser
    fCheckAppIconPath
    /Library/Application\ Support/JAMF/bin/jamfHelper.app/Contents/MacOS/jamfHelper -windowType utility -windowPosition ur -icon "$iconPath" -iconSize 50 -heading "Update Available" -description "$4 must be quit in order to update. Save all data before quitting." -button1 "Quit & Update" -button2 "No Deferrals Left" -timeout 3600
    fQuitApp
    /usr/local/bin/jamf policy -event "$8"
    reopenAnswer="$(/Library/Application\ Support/JAMF/bin/jamfHelper.app/Contents/MacOS/jamfHelper -windowType utility -windowPosition ur -icon "$iconPath" -iconSize 50 -description "$4 has been updated" -button1 "Reopen" -button2 "Ok" -timeout 60)"
    if [[ $reopenAnswer == "0" ]]; then
        fOpenApp
    fi
    #reset timer after updating
    echo "2" > /Library/Application\ Support/JAMF/.$5.timer

else
    # app is not open, reset timer and run updates
    echo "2" > /Library/Application\ Support/JAMF/.$5.timer
    /usr/local/bin/jamf policy -event "$8"
fi

 

Thanks @kendalljjohnson, I am working on adapting this to my needs. Great stuff you put together in your script.

-steven

tend·er (tĕn′dər) noun: One who tends something.

allpurposeben
New Contributor

Am I correct in understanding that using this alternative workflow would mean that a pending patch would not appear in Self Service.app? For example:

  1. There's a policy to upgrade Chrome, set to run daily, running the script.
  2. Chrome Update Policy runs while Chrome is running.
  3. User gets notification to close Chrome and proceed or defer.
  4. They choose to defer
  5. A couple hours pass, it's lunch time, the user decides they are free to run updates now.
  6. They go to Self Service but since there's no actual patch policy in play, there won't be pending patch installs in Self Service.

Is that correct or am I missing something? The user would only ever have the opportunity to trigger the update when the script runs and asks them?

kendalljjohnson
Contributor II

@allpurposeben Correct, I never built in a logic for Self Service. It would need to be a separate policy created since the alerting policy is only available to be run once a day. Theoretically you could clone the daily alerting policy but change it to be ongoing within Self Service, using the same scope of only available to those not on the current version.