Skip to main content
Jamf Nation, hosted by Jamf, is the largest Apple IT management community in the world. Dialog with your fellow IT professionals, gain insight about Apple device deployments, share best practices and bounce ideas off each other. Join the conversation.

jamfhelper software update trigger

So I stole the script from https://jamfnation.jamfsoftware.com/featureRequest.html?id=751 and tweaked the wording a bit.

#!/bin/sh

HELPER=`/Library/Application\ Support/JAMF/bin/jamfHelper.app/Contents/MacOS/jamfHelper -windowType utility -icon /Library/Application\ Support/JAMF/bin/jamfHelper.app/Contents/Resources/Message.png -heading "Software Updates are Available" -description "Would you like to install updates?  System may need to be rebooted." -button2 "Install" -button1 "Cancel" -cancelButton "1"`


      echo "jamf helper result was $HELPER";

      if [ "$HELPER" == "2" ]; then
         /usr/sbin/jamf policy -trigger SWUJH
         exit 0
      else
         echo "user chose No";   
     exit 1
      fi

I also tweaked the cancel button due to when I was using it in it's default form everything returned "user chose No" and it wouldn't run the trigger.

Now when I select install, the box disappears then immediately reappears and I have to choose install again, and then teh box stays gone, but the trigger never starts the updates.

Like Comment
Order by:
SOLVED Posted: by acdesigntech

Hey John, I went a few steps further and added some logic into the script to count how many times the user chooses no before the script runs automatically anyway. To me, it gives the user a greater sense of control even if only perceived.

#!/bin/sh

fRunUpdates ()
{

    ## Once the user OKs the updates or they run automatically, reset the timer to 5 
    echo "5" > /Library/Application\ Support/JAMF/.SoftwareUpdateTimer.txt

    /Library/Application\ Support/JAMF/bin/jamfHelper.app/Contents/MacOS/jamfHelper -windowType hud -lockhud -heading 'ISD is updating software on your computer' -description 'We are now updating your Mac System software. These updates should not take longer than 30 to 45 minutes depending on how many updates your Mac needs. If you see this screen for more than 45 minutes please call our Service Desk at X4949. Please do not turn off this computer. This message will go away when updates are complete.' -icon /Library/Application\ Support/JAMF/EndUserSupport/AGRose.icns > /dev/null 2>&1 &

    ## We'll need the pid of jamfHelper to kill it once the updates are complete
    JHPID=`echo "$!"`

    /usr/sbin/jamf policy -trigger SoftwareUpdate & 
    ## Get the Process ID of the last command run in the background ($!) and wait for it to complete (wait)
    SUPID=`echo "$!"`
    wait $SUPID

    ## kill the jamfHelper. If a restart is needed, the user will be prompted. If not the hud will just go away 
    kill -s KILL $JHPID
    exit 0
}



######### Set variables for the script ############

########## Get the group membership for the client #####################
## Get MAC Address using networksetup
MAC=$( networksetup -getmacaddress en0 | awk '{ print $3 }' | sed 's/:/./g' )

## Use the JSS API to get the Mac's group memberships
JSSGroups=$( curl -s -u username:password https://<casper server>:8443/JSSResource/computers/macaddress/$MAC \
| xpath //computer/groups_accounts/computer_group_memberships[1] \
| sed -e 's/<computer_group_memberships>//g;s/<\/computer_group_memberships>//g;s/<group>//g;s/<\/group>/\n/g' )

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

## Get the timer value
Timer=`cat /Library/Application\ Support/JAMF/.SoftwareUpdateTimer.txt`

## Get the currently logged in user, if any. Also check for updates that require a restart and ones that do not.
UpdatesNoRestart=`softwareupdate -l | grep recommended | grep -v restart`
RestartRequired=`softwareupdate -l | grep restart | grep -v '\*' | cut -d , -f 1`
LoggedInUser=`who | grep console | awk '{print $1}'`

################ End Variable Set ################

## Use echo and grep to find known-core (non system) software update groups. If these groups are found, run these installers silently since no restarts are required for these updates. Use an array to see which updates we take account of. The names of the array elements are also trigger names for each update. This way when there's a new software package to keep updated, we add the trigger name into the array, and the update policy to the JSS. Casper does the rest
NonSysCore=( 'SoftwareUplift-FlashPlayer' 'SoftwareUplift-Flip4Mac' 'SoftwareUplift-FontNuke' 'SoftwareUplift-PrintWindow' 'SoftwareUplift-MicrosoftOffice' 'SoftwareUplift-MicrosoftOutlook' )

for (( i = 0; i < ${#NonSysCore[@]}; i++ ))
do
    CheckUpdate=`echo "$JSSGroups" | grep "${NonSysCore[$i]}"`
    if [ "$CheckUpdate" != "" ]; then
        jamf policy -trigger "${NonSysCore[$i]}"
    fi
done

## If there are no system updates, quit
if [ "$UpdatesNoRestart" == "" -a "$RestartRequired" == "" ]; then
    echo "No updates at this time"
    exit 0
fi

## If we get to this point and beyond, there are updates. 
## if there is no one logged in, just run the updates
if [ "$LoggedInUser" == "" ]; then
    /usr/sbin/jamf policy -trigger SoftwareUpdate
else
    ## someone is logged in. prompt if any updates require a restart ONLY IF the update timer has not reached zero
    if [ "$RestartRequired" != "" ]; then
            ## If someone is logged in and they have not canceled 5 times already, prompt them to install updates that require a restart and state how many more times they can press 'cancel' before updates run automatically.
        if [ $Timer -gt 0 ]; then
            HELPER=`/Library/Application\ Support/JAMF/bin/jamfHelper.app/Contents/MacOS/jamfHelper -windowType utility -icon /Library/Application\ Support/JAMF/EndUserSupport/AGRose.icns -heading "AG ISD Approved Software Updates are Available for Your Mac" -description "These updates will require you to restart your Mac. If you would like to install these now, click 'Install Updates.' If you would not like to install now, click 'Cancel Updates.' You may choose not to install updates $Timer more times before this computer will automatically install them. These updates require a restart of your Mac: $RestartRequired" -button1 "Install Updates" -button2 "Cancel Updates" -cancelButton "2" -defaultButton 2 -timeout 60`
            echo "jamf helper result was $HELPER";
            ## If they click Install Updates then run the updates
            if [ "$HELPER" == "0" ]; then
                fRunUpdates
            else
            ## If no, then reduce the timer by 1. The script will run again the next day 
                let CurrTimer=$Timer-1
                echo "user chose No"
                echo "$CurrTimer" > /Library/Application\ Support/JAMF/.SoftwareUpdateTimer.txt
                exit 1
            fi
        else
            ## If Timer is already 0, run the updates automatically, the user has been warned!
            fRunUpdates
        fi
    fi
fi

## Install updates that do not require a restart
if [ "$UpdatesNoRestart" != "" ]; then
    /usr/sbin/jamf policy -trigger SoftwareUpdate 
fi
Like
SOLVED Posted: by jwojda

That is awesome, that was the next step my bosses wanted me to do (the countdown to forced install).

Like
SOLVED Posted: by jarednichols

This does look great indeed. I was in the middle of crafting a very similar solution so i think I'll give this a try.

Like
SOLVED Posted: by talkingmoose
To me, it gives the user a greater sense of control even if only perceived.

That just made my day! :-)

Nice improvements to the script too!

Like
SOLVED Posted: by Cem

great! it was on my to do list.... thanks!

Like
SOLVED Posted: by lisacherie

Because it might help people..
I originally posted an extract of the script.. this is the whole original script with extra logic to prevent the disappearing jamf helper. I like the extra modifications you have done and may add to mine.

#!/bin/sh

########
# This script checks to see if there is a logged in user, if no user is logged in software update will run and install all udpates.
# If a user is logged in the script checks if the updates need a restart - if so the user is prompted whether this is ok before proceeding.
# Where no restart is required software update will run without interrupting the user.

#see if user logged in
USER=`/usr/bin/who | /usr/bin/grep console | /usr/bin/cut -d " " -f 1`;
echo "logged in user is $USER...";

#check if user logged in

if [ -n "$USER" ]; then

   #Check if restarts required
   AVAILABLEUPDATES=`/usr/sbin/softwareupdate --list`;
   NOUPDATES=`echo $AVAILABLEUPDATES | wc -l | cut -d " " -f 8`;
   RESTARTREQUIRED=`echo $AVAILABLEUPDATES | /usr/bin/grep restart | /usr/bin/cut -d "," -f 1`;

   if [ -n "$RESTARTREQUIRED" ]; then
      echo "$RESTARTREQUIRED needs a restart";

      #ask user whether ok to restart

      OKTORESTART=`/Library/Application\ Support/JAMF/bin/jamfHelper.app/Contents/MacOS/jamfHelper -windowType utility -icon /Library/Application\ Support/JAMF/bin/jamfHelper.app/Contents/Resources/Message.png -heading "Software Updates are Available" -description "Your computer will need to restart, would you like to install the udpates now?" -button1 "Yes" -button2 "Cancel" -cancelButton "2"`

      echo "ok to restart was $OKTORESTART";

      if [ "$OKTORESTART" == "0" ]; then
         /usr/sbin/jamf policy -trigger runsoftwareupdate
         exit 0
      else
         echo "User said NO to updates";   
     exit 1
      fi
   else
      echo "No restart is needed";

      if [ "$NOUPDATES" != "1" ]; then
         echo "So running Software Update"; 
         /usr/sbin/jamf policy -trigger runsoftwareupdate
         exit 0
      else
         echo "because there were no updates";
         exit 0
      fi
   fi

else
   #No logged in user
   /usr/sbin/jamf policy -trigger runsoftwareupdate
fi

exit 0
Like
SOLVED Posted: by jwojda

For some reason when I throw it on a test box, the script ends before JAMFHelper even appears, so when I click on Yes to install they don't do anything. I have the script set to run "before"

Running script softwareupdate_jamf_helper.sh...
Script exit code: 1
Script result: logged in user is jwojda...
Software Update Tool Copyright 2002-2010 Apple Software Update found the following new or updated software: Desktop Documents Library Movies Music Pictures Public OSXUpd10.8.2-10.8.2 OS X Update (10.8.2) needs a restart
ok to restart was 
User said NO to updates

    Unmounting file server...

Lisa/ACDesign,

Is it possible to incorporate the countdown timer from the other script into this?

Like
SOLVED Posted: by acdesigntech

Hey John, I was getting the same output when capturing softwareupdate -l into a variable. For some reason it wants to list the contents of your home folder. Weird, but I also noticed that all updates are [recommended]. If i do a softwareupdate -l | grep recommended and stuff that into a variable, it gives me the right output:

updates=`softwareupdate -l | grep recommended`
ag2025:~ acaldwell$ echo $updates
Remote Desktop Admin Update (3.5.3), 25748K [recommended] Digital Camera Raw Compatibility Update (3.14), 8012K [recommended] Digital Camera Raw Compatibility Update (3.12), 7740K [recommended] Security Update 2012-004 (1.0), 263587K [recommended] [restart] Digital Camera Raw Compatibility Update (3.13), 7979K [recommended] Remote Desktop Admin Update (3.5.2), 25738K [recommended] HP Printer Software Update (2.9), 8740K [recommended]

Unfortunately, you won't be able to grep out the [restart] without running softwareupdate -l again since this method throws it all into a single line.

Like
SOLVED Posted: by lisacherie

That awkward moment when you look back at a script you wrote some time ago, and decide you don't like the logic/style that you thought was a great idea at the time.. A revised version which I'm testing.. removes some duplicate checks, and less noise.

#!/usr/bin/perl -w

use strict;

my $AVAILABLEUPDATES="";

$AVAILABLEUPDATES=`/usr/sbin/softwareupdate --list`;
chomp $AVAILABLEUPDATES;

printf "available updates is %s \n\n", "$AVAILABLEUPDATES";

# If available updates contains * there are updates available

if ($AVAILABLEUPDATES=~/\*/){

    printf "there are updates available\n";

    if ($AVAILABLEUPDATES=~/restart/){

        printf "updates need a restart\n";

        my $LOGGEDINUSER='';

        $LOGGEDINUSER=`/usr/bin/who | /usr/bin/grep console | /usr/bin/cut -d " " -f 1`;
        chomp $LOGGEDINUSER;

        printf "value of logged in user is $LOGGEDINUSER..\n";

        if ($LOGGEDINUSER=~/[a-zA-Z]/) {

            printf "as there is a logged in user checking whether ok to restart\n";

            my $RESPONSE = "";

            $RESPONSE=`'/Library/Application Support/JAMF/bin/jamfHelper.app/Contents/MacOS/jamfHelper' -windowType utility -icon '/Library/Application Support/JAMF/bin/jamfHelper.app/Contents/Resources/Message.png' -heading "Software Updates are available" -description "Your computer will need to restart, would you like to install the updates now?" -button1 "Yes" -button2 "Cancel" -cancelButton "2"`;

            printf "response was $RESPONSE\n";

            if ($RESPONSE == "0") {
                printf "User said YES to Updates\n";
                system "/usr/sbin/jamf policy -trigger runsoftwareupdate";
                exit 0;
            } else {
                printf "User said NO to Updates\n";
                exit 0;
            }
        }
        else {
            printf "no logged in user so ok to run updates\n";
            system "/usr/sbin/jamf policy -trigger runsoftwareupdate";
            exit 0;
        }
    }
    else {
        printf "no restart required\n";
        system "/usr/sbin/jamf policy -trigger runsoftwareupdate";
        exit 0;
    }
}
else {
    printf "there are no updates available\n";
    exit 0;
}
exit 0;
Like
SOLVED Posted: by lisacherie

@jwojda

logic to incorporate the timer would be added after the line:
printf "User said NO to Updates\n";

I am not sure whether I want to force the installation of updates requiring a restart as I don't know what the users might be doing at the time.

Like
SOLVED Posted: by lisacherie

Chatted with the support guys during lunch at JNUC, as I was seeing the script continue before waiting for the return value from jamfhelper.

Adding this to the line which calls jamfhelper is supposed to fix this!

-startlaunchd

Like
SOLVED Posted: by jwojda

The forcing updates is because the users had X amount of days to install them. I understand that sometimes people are busy and can't do them immediately, but I still have to adhere to our corporate patching standards, and I actually like to be ahead of the curve for anything I can be. The mac's are the black sheep of the company, yet all the executives have/want them, so they also have a spotlight on it if something doesn't go as smooth.

I've got the 10.8.x systems running through the MAS currently, and I submitted a request via our Apple Service Agreement to add the countdown timer to force install as well.

Thank you for your continued tweaks and support of the script! I will try it out when i'm back in the office tomorrow.

Like
SOLVED Posted: by UESCDurandal

@lisacherie

Thanks a bunch for making this script! I've been looking all over the place for a solution like this.

It works flawlessly for most users. However, I am encountering instances where I am seeing this in the log:

there are updates available
updates need a restart
value of logged in user is HDConsole..
as there is a logged in user checking whether ok to restart
Argument "" isn't numeric in numeric eq (==) at /private/tmp/softwareupdate_jamf_helper_2.sh line 39.
response was User said YES to Updates
Checking for policies triggered by "runsoftwareupdate"...

The user does not end up seeing a message asking if they want to install, it assumes a Yes response was given.

I'm not an expert in scripting yet, but can the script be changed so that if a user presses "Yes" then a numeric is produced as the trigger to run "jamf policy -trigger runsoftwareupdate" instead of using "Cancel"?

Like
SOLVED Posted: by lisacherie

@UESCDurandal

I've literally just got back to work after the conference.

In the post above there is an extra argument that can be added, which I think will sort that out. I need to add in and test.. I will update this discussion probably early next week, once I've done some testing, and will fix the issues with the script then. (Posting an updated version).

Really glad this is helpful for others :)

Like
SOLVED Posted: by lisacherie

Made the quick changes to the comparison check, and added the startlaunchd.. seems to be running well. Still doing some testing.. But here it is:

#!/usr/bin/perl -w

use strict;

my $AVAILABLEUPDATES="";

$AVAILABLEUPDATES=`/usr/sbin/softwareupdate --list`;
chomp $AVAILABLEUPDATES;

printf "available updates is %s \n\n", "$AVAILABLEUPDATES";

# If available updates contains * there are updates available

if ($AVAILABLEUPDATES=~/\*/){

        printf "there are updates available\n";

        if ($AVAILABLEUPDATES=~/restart/){

                printf "updates need a restart\n";

                my $LOGGEDINUSER='';

                $LOGGEDINUSER=`/usr/bin/who | /usr/bin/grep console | /usr/bin/cut -d " " -f 1`;
                chomp $LOGGEDINUSER;

                printf "value of logged in user is $LOGGEDINUSER..\n";

                if ($LOGGEDINUSER=~/[a-zA-Z]/) {

                        printf "as there is a logged in user checking whether ok to restart\n";

                        my $RESPONSE = "";

                        $RESPONSE=system '\'/Library/Application Support/JAMF/bin/jamfHelper.app/Contents/MacOS/jamfHelper\' -startlaunchd -windowType utility -icon \'/Library/Application Support/JAMF/bin/jamfHelper.app/Contents/Resources/Message.png\' -heading "Software Updates are available" -description "Your computer will need to restart, would you like to install the updates now?" -button1 "Yes" -button2 "Cancel" -cancelButton "2"';

                        if ($RESPONSE eq "0") {
                                printf "\nUser said YES to Updates\n";
                                system "/usr/sbin/jamf policy -trigger runsoftwareupdate";
                                exit 0;
                        } else {
                                printf "\nUser said NO to Updates\n";
                                exit 0;
                        }
                }
                else {
                        printf "no logged in user so ok to run updates\n";
                        system "/usr/sbin/jamf policy -trigger runsoftwareupdate";
                        exit 0;
                }
        }
        else {
                printf "no restart required\n";
                system "/usr/sbin/jamf policy -trigger runsoftwareupdate";
                exit 0;
        }
}
else {
        printf "there are no updates available\n";
        exit 0;
}
exit 0;
Like
SOLVED Posted: by UESCDurandal

Lisa, I've been testing the latest version of your script for a few days now. So far no issues. Thanks again!

Like
SOLVED Posted: by lisacherie

The last version I pasted seems to be going well so far in my testing.
Just some clients dropping off before finishing the download for the larger updates.
Happy for feedback if you see any problems.
Thank you.

Like
SOLVED Posted: by jwojda

I've made a few tweaks because I couldn't get the jamf trigger softwareupdates to work reliably, I could get the script to run when I did a manual jamf policy -id on it, but not when I ran it on the every 15 trigger.

So I submitted a ticket to Jamf and this is what they came back with.

I did some testing on this and found the following.

If I run this script by any of the following it will work:

\- Self Service
\- Manually call the script
\- Manually call the policy via terminal

What will not work

\- Running script on the every15 trigger
\- In terminal logging in as the root user and then running the script

I talked with a co-worker about this we think that this has to do with the root user running this script that is trying to open the MAS from another user.

So the root user can not open the MAS for say the user admin.

I am in no way familiar with Pearl here so what I might suggest is that we find a way to run the command that opens the MAS as the currently logged in user and that might fix our issue here.

Like
SOLVED Posted: by lisacherie

@jwojda

So far in testing I have not seen the issues you have described.
I made a second policy with a custom trigger of runsoftwareupdate, with payload to install all available software updates. this policy is set to ongoing to allow repeated calls.

The first policy which displays the popup is triggered on everyHour, with a frequency of once a day. Most days exiting silently as there are no new updates enabled on SUS.

I will try to reproduce what you have described later in the week. With the previous version I was seeing the popup ignore user input and always taking the cancel option regardless of what was selected. Adding the "-startlaunchd" sorted that out \- doublecheck you have that argument in the line which invokes the jamf helper popup.

Like
SOLVED Posted: by jwojda

I think the jamf trigger command was not working for the same reason, i thought maybe because it was trying to call a 2nd policy, so thats why I changed to just launch the mac app store directly.

#!/usr/bin/perl -w

use strict;

my $AVAILABLEUPDATES="";

$AVAILABLEUPDATES=`/usr/sbin/softwareupdate --list`;
chomp $AVAILABLEUPDATES;

printf "available updates is %s \n\n", "$AVAILABLEUPDATES";

# If available updates contains * there are updates available

if ($AVAILABLEUPDATES=~/\*/){

        printf "there are updates available\n";

        if ($AVAILABLEUPDATES=~/restart/){

                printf "updates need a restart\n";

                my $LOGGEDINUSER='';

                $LOGGEDINUSER=`/usr/bin/who | /usr/bin/grep console | /usr/bin/cut -d " " -f 1`;
                chomp $LOGGEDINUSER;

                printf "value of logged in user is $LOGGEDINUSER..\n";

                if ($LOGGEDINUSER=~/[a-zA-Z]/) {

                        printf "as there is a logged in user checking whether ok to restart\n";

                        my $RESPONSE = "";

                        $RESPONSE=system '\'/Library/Application Support/JAMF/bin/jamfHelper.app/Contents/MacOS/jamfHelper\' -startlaunchd -windowType utility -icon \'/Library/Application Support/JAMF/bin/jamfHelper.app/Contents/Resources/Message.png\' -heading "Software Updates are available" -description "Your computer will need to restart, would you like to install the updates now?" -button1 "Yes" -button2 "Cancel" -cancelButton "2"';

                        if ($RESPONSE eq "0") {
                                printf "\nUser said YES to Updates\n";
                                system '/System/Library/CoreServices/Software\ Update.app/Contents/MacOS/Software\ Update';
                                exit 0;
                        } else {
                                printf "\nUser said NO to Updates\n";
                                exit 0;
                        }
                }
                else {
                        printf "no logged in user so ok to run updates\n";
                        system '/System/Library/CoreServices/Software\ Update.app/Contents/MacOS/Software\ Update';
                        exit 0;
                }
        }
        else {
                printf "no restart required\n";
                system '/System/Library/CoreServices/Software\ Update.app/Contents/MacOS/Software\ Update';
                exit 0;
        }
}
else {
        printf "there are no updates available\n";
        exit 0;
}
exit 0;
Like
SOLVED Posted: by UESCDurandal

Lisa \- I just discovered that when the restart required check looks at the Mac Pro EFI Update 1.5 item it returns the response "shut down" instead of "restart"... Hence my user was prompted to restart his Mac in 5 minutes while he's in the middle of editing in FCP. :(

Is it possible to add "shut down" as a response that will trigger the pop-up box?

available updates is Software Update Tool
Copyright 2002-2009 Apple

Software Update found the following new or updated software:
\* MacProEFIUpdate1.5-1.5
Mac Pro EFI Firmware Update (1.5), 2059K [recommended] [shut down]

there are updates available
no restart required
Checking for policies triggered by "runsoftwareupdate"...

...

Writing files…
Optimizing system for installed software…
Writing package receipts…
Installed Mac Pro EFI Firmware Update
Done.

You have installed one or more updates that requires that you restart your
computer. Please restart immediately.

A reboot was required with one or more of the installed updates.
Blessing i386 OS X System on /...
Creating Reboot Script...

Like
SOLVED Posted: by lisacherie

@UESCDurandal

Good catch! Not many MPs where I am.

Try changing this line:

if ($AVAILABLEUPDATES=~/restart/){

To:

if ($AVAILABLEUPDATES=~/(restart)|(shut\sdown)/){

Let me know if that works for you, I'm not seeing any computers that need that update to test it myself :(

Like
SOLVED Posted: by UESCDurandal

Thanks Lisa!

I'll give that a try. Sadly I already reached out to all my MPs that needed that update and pushed it through manually so they're not interrupted. But it's good to know that the line is there in case Apple wants to do that again.

Like
SOLVED Posted: by acdesigntech

ok, not a perl guy by a long shot. How would I get the PID of the last executed command in a perl script, and then force the script to continue while that one command ran?

ex: ```
/Library/Application\ Support/JAMF/bin/jamfHelper.app/Contents/MacOS/jamfHelper -windowType fs -heading 'American Greetings ISD is updating software on your computer' -description 'We are now updating your Mac System software. This update should not take longer than 30 to 45 minutes, depending on how many updates your Mac needs. If you see this screen for more than 45 minutes, please call our helpdesk at X4949. Please do not turn off this computer. This message will go away when updates are complete.' -icon /Library/Application\ Support/JAMF/EndUserSupport/AGRose.icns > /dev/null 2>&1 &

## In case we need the process ID for the jamfHelper JHPID=echo "$!"
```

Like
SOLVED Posted: by chappj01

Thanks everyone for these. Since I do most of my scripting in Python, I thought I'd take a stab at rewriting it. Hopefully someone will find it useful. Let me know if you have any questions about what it's doing. I've tested it on several systems and haven't run across any showstoppers, but if you find something, please pass it on.

#!/usr/bin/python

# import required modules
import os
import re
import shutil
from datetime import datetime
from sys import exit

# set log filename/location
curDate = datetime.now().date()
logFile = 'swupd-%s-%s.log' % (curDate.year,curDate.month)
logLocation = '/your/preferred/log/directory'
curLog = '%s/%s' % (logLocation,logFile)

# create functions for installing updates, logging postponements, and exiting
def installUpdates():
    os.popen('/usr/sbin/jamf policy -trigger runswupd')
    attempts = 6
    logData(attempts)
    exit(0)

def postponeUpdates(attempts):
    print "User chose to postpone the updates."
    attempts = attempts + 1
    logData(attempts)
    exit(0)

def forceUpdates():
    response = os.popen('"{filename}" -startlaunchd -windowType utility -icon "{icon}" -title "{title}" -heading "{heading}" -description "{description}" -button1 "Ok"'.format(
        description='Your computer has updates availble that may require a reboot.  You\'ve postponed the updates 5 times.  As such, the installation is being forced now.  Please save your work immediately.',
        filename='/Library/Application Support/JAMF/bin/jamfHelper.app/Contents/MacOS/jamfHelper',
        heading='Software Updates are installing!',
        icon='/Library/Application Support/JAMF/bin/jamfHelper.app/Contents/Resources/Message.png',
        title='Company Software Updates'
    )).read()
    if response == 0:
        installUpdates()
    else:
        print "Something went wrong with the jamfHelper.  Fix it."
        attempts = 7
        logData(attempts)
        exit(1)

def logData(content): # writes 'content' to the log, overwriting any previous contents.  There should never be anything other than a single number in there anyway. 1 - 5 indicates the number of times it has been postponed.  6 indicates complete for the month.  7 indicates the forced attempt at updates failed.
    logContent = open(curLog, 'w')
    logContent.write(str(content))
    logContent.close()


# create log path if it doesn't exist
if not os.path.isdir(logLocation):
    os.makedirs(logLocation)

# create monthly log file if it doesn't exist and read file contents if it does
if not os.path.isdir(curLog) and not os.path.exists(curLog):
    logNew = open(curLog, 'w')
    attempts = 0
    logNew.write(str(attempts))
    logNew.close()
elif os.path.isdir(curLog):
    print "The path for the current log file was a directory.  How this happened, the world may never know.  The path %s, and everything under it is now toast, and will be recreated as a file." % curLog
    shutil.rmtree(curLog)
    logNew = open(curLog, 'w')
    attempts = 0
    logNew.write(str(attempts))
    logNew.close()
elif os.path.isfile(curLog): # read the number of attempts that have been postponed
    logIn = open(curLog, 'r+')
    try:
        attempts = int(logIn.read())
    except ValueError:
        attempts = 0
        logIn.write(str(attempts))
    logIn.close()


# check for updates
updatesAvail = os.popen('/usr/sbin/softwareupdate --list').read().rstrip()
print "Available updates: %s" % updatesAvail
if "*" in updatesAvail:
    print "There are updates available"
    os.popen('/usr/sbin/jamf recon') # running recon to make sure the JSS knows the system has updates available.  In theory, it should always know.
    avail = 1
else:
    avail = 0

if avail == 0:
    print "No updates were available"
    os.popen('/usr/sbin/jamf recon') # running recon to make sure the JSS knows that there really aren't updates available.
    logData('complete')
    exit(0)

# check if available updates require a restart    
if 'restart' in updatesAvail or 'shut down' in updatesAvail:
    print "These updates require a restart"
    restart = 1
else:
    restart = 0

# find currently logged in console user
curUser = os.popen('/usr/bin/who | grep console | cut -d " " -f 1').read().rstrip()
print "Current user is: %s" % curUser

# if there is a logged in user, we must ask for permission to restart, otherwise we go forward!
if avail == 1:
    if attempts < 5:
        if restart == 1:
            if re.match(r'[a-zA-Z]', curUser):
                response = int(os.popen('"{filename}" -startlaunchd -windowType utility -icon "{icon}" -title "{title}" -heading "{heading}" -description "{description}" -button1 "Yes" -button2 "No" -cancelButton "2"'.format(
                    description='Your computer has updates availble that will require a reboot.  Would you like to install these updates now?  After clicking yes, please wait until you receive an indication that the computer is ready to reboot.',
                    filename='/Library/Application Support/JAMF/bin/jamfHelper.app/Contents/MacOS/jamfHelper',
                    heading='Software Updates are available!',
                    icon='/Library/Application Support/JAMF/bin/jamfHelper.app/Contents/Resources/Message.png',
                    title='Company Software Updates'
                )).read())
            else:
                installUpdates()
            if response == 0:
                installUpdates()
            elif response == 2:
                postponeUpdates(attempts)
            elif response == 1:
                print "There was a problem spawning the dialog box.  You need to make sure the system is properly enrolled in Casper."
                exit(1)
            else:
                print "Something that should be impossible happened. Your return code was %s.  Please check the code against the jamfHelper return values by running jamfHelper with the -help flag." % response
                exit(1)
        else:
            installUpdates()
    elif attempts == 5:
            forceUpdates()
    else:
        print "Something unknown is wrong with this machine.  Exiting with an error status code."
        exit(1)
Like
SOLVED Posted: by rderewianko

Great Script chappj0 might add a modifier to state how many times they can click cancel before they're forced but it was more or less what I was looking for..

Noticed a typo on your description.. Your computer has updates availble should be available

Other than that seems to be working fine for me. Thanks!

Like
SOLVED Posted: by chappj01

I've updated it to add the counter you were looking for, as well as to fix the spelling issues. Let me know if you find anything else!

#!/usr/bin/python

# import required modules
import os
import re
import shutil
from datetime import datetime
from sys import exit

# set log filename/location
curDate = datetime.now().date()
logFile = 'swupd-%s-%s.log' % (curDate.year,curDate.month)
logLocation = '/Your/Preferred/Log/Location'
curLog = '%s/%s' % (logLocation,logFile)

# create functions for installing updates, logging postponements, and exiting
def installUpdates():
    os.popen('/usr/sbin/jamf policy -trigger runswupd')
    attempts = 6
    logData(attempts)
    exit(0)

def postponeUpdates(attempts):
    print "User chose to postpone the updates."
    attempts = attempts + 1
    logData(attempts)
    exit(0)

def forceUpdates():
    response = int(os.popen('"{filename}" -startlaunchd -windowType utility -icon "{icon}" -title "{title}" -heading "{heading}" -description "{description}" -button1 "Ok"'.format(
        description='Your computer has updates available that may require a reboot.  You\'ve postponed the updates 5 times.  As such, the installation is being forced now.  Please save your work immediately.',
        filename='/Library/Application Support/JAMF/bin/jamfHelper.app/Contents/MacOS/jamfHelper',
        heading='Software Updates are installing!',
        icon='/Library/Application Support/JAMF/bin/jamfHelper.app/Contents/Resources/Message.png',
        title='Company Software Updates'
    )).read())
    if response == 0:
        installUpdates()
    else:
        print "Something went wrong with the jamfHelper.  Fix it."
        attempts = 7
        logData(attempts)
        exit(1)

def logData(content): # writes 'content' to the log, overwriting any previous contents.  There should never be anything other than a single number in there anyway. 1 - 5 indicates the number of times it has been postponed.  6 indicates complete for the month.  7 indicates the forced attempt at updates failed.
    logContent = open(curLog, 'w')
    logContent.write(str(content))
    logContent.close()


# create log path if it doesn't exist
if not os.path.isdir(logLocation):
    os.makedirs(logLocation)

# create monthly log file if it doesn't exist and read file contents if it does
if not os.path.isdir(curLog) and not os.path.exists(curLog):
    logNew = open(curLog, 'w')
    attempts = 0
    logNew.write(str(attempts))
    logNew.close()
elif os.path.isdir(curLog):
    print "The path for the current log file was a directory.  How this happened, the world may never know.  The path %s, and everything under it is now toast, and will be recreated as a file." % curLog
    shutil.rmtree(curLog)
    logNew = open(curLog, 'w')
    attempts = 0
    logNew.write(str(attempts))
    logNew.close()
elif os.path.isfile(curLog): # read the number of attempts that have been postponed
    logIn = open(curLog, 'r+')
    try:
        attempts = int(logIn.read())
    except ValueError:
        attempts = 0
        logIn.write(str(attempts))
    logIn.close()


# check for updates
updatesAvail = os.popen('/usr/sbin/softwareupdate --list').read().rstrip()
print "Available updates: %s" % updatesAvail
if "*" in updatesAvail:
    print "There are updates available"
    os.popen('/usr/sbin/jamf recon') # running recon to make sure the JSS knows the system has updates available.  In theory, it should always know.
    avail = 1
else:
    avail = 0

if avail == 0:
    print "No updates were available"
    os.popen('/usr/sbin/jamf recon') # running recon to make sure the JSS knows that there really aren't updates available.
    logData('6')
    exit(0)

# check if available updates require a restart    
if 'restart' in updatesAvail or 'shut down' in updatesAvail:
    print "These updates require a restart"
    restart = 1
else:
    restart = 0

# find currently logged in console user
curUser = os.popen('/usr/bin/who | grep console | cut -d " " -f 1').read().rstrip()
print "Current user is: %s" % curUser

# if there is a logged in user, we must ask for permission to restart, otherwise we go forward!
postponesRemaining = 5 - int(attempts)
if avail == 1:
    if attempts < 5:
        if restart == 1:
            if re.match(r'[a-zA-Z]', curUser):
                response = int(os.popen('"{filename}" -startlaunchd -windowType utility -icon "{icon}" -title "{title}" -heading "{heading}" -description "{description}" -button1 "Yes" -button2 "No" -cancelButton "2"'.format(
                    description='Your computer has updates available that will require a reboot.  Would you like to install these updates now?  After clicking yes, please wait until you receive an indication that the computer is ready to reboot. You can postpone up to %s more times before installations are forced.' % postponesRemaining,
                    filename='/Library/Application Support/JAMF/bin/jamfHelper.app/Contents/MacOS/jamfHelper',
                    heading='Software Updates are available!',
                    icon='/Library/Application Support/JAMF/bin/jamfHelper.app/Contents/Resources/Message.png',
                    title='Company Software Updates'
                )).read())
            else:
                installUpdates()
            if response == 0:
                installUpdates()
            elif response == 2:
                postponeUpdates(attempts)
            elif response == 1:
                print "There was a problem spawning the dialog box.  You need to make sure the system is properly enrolled in Casper."
                exit(1)
            else:
                print "Something that should be impossible happened. Your return code was %s.  Please check the code against the jamfHelper return values by running jamfHelper with the -help flag." % response
                exit(1)
        else:
            installUpdates()
    elif attempts == 5:
            forceUpdates()
    else:
        print "Something unknown is wrong with this machine.  Exiting with an error status code."
        exit(1)
Like
SOLVED Posted: by chappj01

Just edited the above post with one tiny bug fix in the forceUpdates() function, in case anyone has already copied this for use.

Like
SOLVED Posted: by acdesigntech

In case anyone was interested, here's my bash version of the script. I edited it to run updates that did NOT require restarts in the background without prompting the user, and only prompt if an update requires a restart. I also incorporated @mm2270][/url][/url's awesome API call to get the group memberships of the Mac. Using some "clever" policy trigger and group naming conventions, I can fully automate the software update process for both system and non system software.

#!/bin/sh

fRunUpdates ()
{

    ## Once the user OKs the updates or they run automatically, reset the timer to 5 
    echo "5" > /Library/Application\ Support/JAMF/.SoftwareUpdateTimer.txt

    /Library/Application\ Support/JAMF/bin/jamfHelper.app/Contents/MacOS/jamfHelper -windowType hud -lockhud -heading 'ISD is updating software on your computer' -description 'We are now updating your Mac System software. These updates should not take longer than 30 to 45 minutes depending on how many updates your Mac needs. If you see this screen for more than 45 minutes please call our Service Desk at X4949. Please do not turn off this computer. This message will go away when updates are complete.' -icon /Library/Application\ Support/JAMF/EndUserSupport/AGRose.icns > /dev/null 2>&1 &

    ## We'll need the pid of jamfHelper to kill it once the updates are complete
    JHPID=`echo "$!"`

    /usr/sbin/jamf policy -trigger SoftwareUpdate & 
    ## Get the Process ID of the last command run in the background ($!) and wait for it to complete (wait)
    SUPID=`echo "$!"`
    wait $SUPID

    ## kill the jamfHelper. If a restart is needed, the user will be prompted. If not the hud will just go away 
    kill -s KILL $JHPID
    exit 0
}



######### Set variables for the script ############

########## Get the group membership for the client #####################
## Get MAC Address using networksetup
MAC=$( networksetup -getmacaddress en0 | awk '{ print $3 }' | sed 's/:/./g' )

## Use the JSS API to get the Mac's group memberships
JSSGroups=$( curl -s -u username:password https://<casper server>:8443/JSSResource/computers/macaddress/$MAC \
| xpath //computer/groups_accounts/computer_group_memberships[1] \
| sed -e 's/<computer_group_memberships>//g;s/<\/computer_group_memberships>//g;s/<group>//g;s/<\/group>/\n/g' )

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

## Get the timer value
Timer=`cat /Library/Application\ Support/JAMF/.SoftwareUpdateTimer.txt`

## Get the currently logged in user, if any. Also check for updates that require a restart and ones that do not.
UpdatesNoRestart=`softwareupdate -l | grep recommended | grep -v restart`
RestartRequired=`softwareupdate -l | grep restart | grep -v '\*' | cut -d , -f 1`
LoggedInUser=`who | grep console | awk '{print $1}'`

################ End Variable Set ################

## Use echo and grep to find known-core (non system) software update groups. If these groups are found, run these installers silently since no restarts are required for these updates. Use an array to see which updates we take account of. The names of the array elements are also trigger names for each update. This way when there's a new software package to keep updated, we add the trigger name into the array, and the update policy to the JSS. Casper does the rest
NonSysCore=( 'SoftwareUplift-FlashPlayer' 'SoftwareUplift-Flip4Mac' 'SoftwareUplift-FontNuke' 'SoftwareUplift-PrintWindow' 'SoftwareUplift-MicrosoftOffice' 'SoftwareUplift-MicrosoftOutlook' )

for (( i = 0; i < ${#NonSysCore[@]}; i++ ))
do
    CheckUpdate=`echo "$JSSGroups" | grep "${NonSysCore[$i]}"`
    if [ "$CheckUpdate" != "" ]; then
        jamf policy -trigger "${NonSysCore[$i]}"
    fi
done

## If there are no system updates, quit
if [ "$UpdatesNoRestart" == "" -a "$RestartRequired" == "" ]; then
    echo "No updates at this time"
    exit 0
fi

## If we get to this point and beyond, there are updates. 
## if there is no one logged in, just run the updates
if [ "$LoggedInUser" == "" ]; then
    /usr/sbin/jamf policy -trigger SoftwareUpdate
else
    ## someone is logged in. prompt if any updates require a restart ONLY IF the update timer has not reached zero
    if [ "$RestartRequired" != "" ]; then
            ## If someone is logged in and they have not canceled 5 times already, prompt them to install updates that require a restart and state how many more times they can press 'cancel' before updates run automatically.
        if [ $Timer -gt 0 ]; then
            HELPER=`/Library/Application\ Support/JAMF/bin/jamfHelper.app/Contents/MacOS/jamfHelper -windowType utility -icon /Library/Application\ Support/JAMF/EndUserSupport/AGRose.icns -heading "AG ISD Approved Software Updates are Available for Your Mac" -description "These updates will require you to restart your Mac. If you would like to install these now, click 'Install Updates.' If you would not like to install now, click 'Cancel Updates.' You may choose not to install updates $Timer more times before this computer will automatically install them. These updates require a restart of your Mac: $RestartRequired" -button1 "Install Updates" -button2 "Cancel Updates" -cancelButton "2" -defaultButton 2 -timeout 60`
            echo "jamf helper result was $HELPER";
            ## If they click Install Updates then run the updates
            if [ "$HELPER" == "0" ]; then
                fRunUpdates
            else
            ## If no, then reduce the timer by 1. The script will run again the next day 
                let CurrTimer=$Timer-1
                echo "user chose No"
                echo "$CurrTimer" > /Library/Application\ Support/JAMF/.SoftwareUpdateTimer.txt
                exit 1
            fi
        else
            ## If Timer is already 0, run the updates automatically, the user has been warned!
            fRunUpdates
        fi
    fi
fi

## Install updates that do not require a restart
if [ "$UpdatesNoRestart" != "" ]; then
    /usr/sbin/jamf policy -trigger SoftwareUpdate 
fi
Like
SOLVED Posted: by maik.sanftenberg

Hi.
I want to bring this one up as I like this kind of solution quite a lot.
I'm testing this at the moment and it seems that somehow there seems to be a minor issue.

We are running on Casper. 6.82

If I run this via a policy I'm able to see in the Logs section that it run fine.

/usr/sbin/jamf is version 8.62 Executing Policy Check for Software Updates... Mounting afp://XXX.XXX.XXX.XXX/Casper_Share to /Volumes/Casper_Share... Running script check_software_updates.pl... Script exit code: 0 Script result: 2013-02-06 12:49:04.316 softwareupdate[642:3803] No alternate URLs found for packageId amds 2013-02-06 12:49:04.330 softwareupdate[642:3803] No alternate URLs found for packageId com.apple.pkg.MakeQueuesScript available updates is Software Update Tool Copyright 2002-2010 Apple Software Update found the following new or updated software: \ iTunesX-11.0.1 iTunes (11.0.1), 193391K [recommended] \ 041-9116-2.13 HP Printer Software Update (2.13), 79868K [recommended] there are no updates available Unmounting file server...

But, I did not get any information screen that updates are available or can cancel it.
I'm not so familiar with scripting so sorry for that maybe stupid question.

Like
SOLVED Posted: by jhbush1973

Andrew, I'm wondering what your SoftwareUpdate trigger/policy looks like if you can share.

Like
SOLVED Posted: by acdesigntech

@maik \- you won't get a dialog box when you run softwareupdate from the command line. It runs silently in the background.

@jhbush \- Sure. Here';s the policy for the Software Update (both system updates and manual non-system software updates):

Name: Casper Assisted Software Uplift
Active: Yes
Frequency: Once every Day
Trigger: every15
Scope: (I do my pilot group for a week, then my extended pilot group, then release to the general population)
Plan: Run Script AG Software Update 1.0 (just what I called the above script)

Here's one of my manual software update policies (gets called based on the Mac's group membership, discovered in the script above):

Name: SoftwareUplift-Flip4Mac 2.4.4.2
Active: Yes
Frequency: Ongoing
Trigger: SoftwareUplift-Flip4Mac
Scope: SoftwareUplift-Flip4Mac 2.4.4.2
Plan: Install Telestream Flip4Mac 2.4.4.2, Install Microsoft Silverlight 5.1.10.411, Update inventory, Display Message: Flip4Mac player has been updated. Please quit safari and re-open it

Like
SOLVED Posted: by stevewood

Maik if the updates do not require a restart the jamfHelper will not show up. The updates simply run in the background.

The log you show has updates that do jot require a restart. Find a machine that has some that do and you'll see jamfHelper.

Like
SOLVED Posted: by maik.sanftenberg

Thanks Steve.
I will see if I get a machine that requiere more updates ;-). But I think that might be a little bit tricky.

Like
SOLVED Posted: by jhbush1973

Andrew, thanks for that. Would you mind sharing the update script as well? I'm a bit lost on the logic part.

Like
SOLVED Posted: by Sonic84

Silly question, is there a reason running jamfHelper commands via a SSH connection would result in no return values?

Like
SOLVED Posted: by lisacherie

Try using this argument when calling the jamfHelper

-startlaunchd
Like
SOLVED Posted: by lisacherie

Had to put in the countdown timer.. Here is a new version with the countdown, and customised messages to let the user know which attempt they are at (along with the compulsory reboot message when all attempts are reached).

Currently set to 3 attempts \- change variable at top to customise if you want to be nicer.

Seems ok so far in testing.. but happy for feedback!

#!/usr/bin/perl -w

use strict;

my $AVAILABLEUPDATES="";
my $CHANCESTOUPDATE=3;
my $COUNTFILE='/etc/SUScount.txt';
my $UPDATECOUNT=0;


$AVAILABLEUPDATES=`/usr/sbin/softwareupdate --list`;
chomp $AVAILABLEUPDATES;

printf "available updates is %s \n\n", "$AVAILABLEUPDATES";

unless (-e $COUNTFILE){
    system "/bin/echo 0 > $COUNTFILE";
} 

$UPDATECOUNT=`/bin/cat $COUNTFILE`;
printf "update count is $UPDATECOUNT\n\n";

# If available updates contains * there are updates available

if ($AVAILABLEUPDATES=~/\*/){

    printf "there are updates available\n";

    if ($AVAILABLEUPDATES=~/(restart)|(shut\sdown)/){

        printf "updates need a restart\n";

        my $LOGGEDINUSER='';

        $LOGGEDINUSER=`/usr/bin/who | /usr/bin/grep console | /usr/bin/cut -d " " -f 1`;
        chomp $LOGGEDINUSER;

        printf "value of logged in user is $LOGGEDINUSER..\n";

        if ($LOGGEDINUSER=~/[a-zA-Z]/) {

            printf "as there is a logged in user checking count\n";

            my $CHANCESLEFT=$CHANCESTOUPDATE - $UPDATECOUNT;
            printf "Chances left is $CHANCESLEFT\n";      

            if ($CHANCESLEFT <= 0) {
                printf "No chances left installing updates and will reboot..\n";
                system '/usr/sbin/jamf displayMessage -message "Your computer is installing updates and will reboot shortly."';
                system "/usr/sbin/jamf policy -trigger runsoftwareupdate";
                system "/bin/echo 0 > $COUNTFILE";
                exit 0;
            }

            else {
                printf "$CHANCESLEFT chances left... checking whether ok to restart\n";
                my $ATTEMPT = $UPDATECOUNT + 1;
                my $FINAL = $CHANCESTOUPDATE + 1;

                my $COMMAND= "\'/Library/Application Support/JAMF/bin/jamfHelper.app/Contents/MacOS/jamfHelper\' -startlaunchd -windowType utility -icon \'/Library/Application Support/JAMF/bin/jamfHelper.app/Contents/Resources/Message.png\' -heading \"Software Updates are Available for Your Computer\" -description \"This is installation attempt $ATTEMPT of $CHANCESTOUPDATE. On attempt $FINAL, updates will install automatically and your machine will restart.\" -button1 \"Yes\" -button2 \"Defer\" -cancelButton \"2\"";

                my $RESPONSE = "";
                $RESPONSE=system $COMMAND;

                if ($RESPONSE eq "0") {
                    printf "\nUser said YES to Updates\n";
                    system "/usr/sbin/jamf policy -trigger runsoftwareupdate";
                    system "/bin/echo 0 > $COUNTFILE";
                    exit 0;
                } else {
                    printf "\nUser said NO to Updates update count is $UPDATECOUNT\n";
                    $UPDATECOUNT=$UPDATECOUNT + 1;
                    system "/bin/echo $UPDATECOUNT > $COUNTFILE";
                    printf "\nUpdate count is now $UPDATECOUNT\n";
                    exit 0;
                }
            }
        } else {
            printf "no logged in user so ok to run updates\n";
            system "/usr/sbin/jamf policy -trigger runsoftwareupdate";
            system "/bin/echo 0 > $COUNTFILE";
            exit 0;
        }
    } 
    else {
        printf "no restart required\n";
        system "/usr/sbin/jamf policy -trigger runsoftwareupdate";
        system "/bin/echo 0 > $COUNTFILE";
        exit 0;
    }
}
else {
    printf "there are no updates available\n";
    system "/bin/echo 0 > $COUNTFILE";
    exit 0;
}
exit 0;
Like
SOLVED Posted: by acdesigntech

@jhbush1973 -- sure I can give a little explanation.

So first we get the Mac address of the Mac. This will be used in the API call to get group memberships for that computer. (first run the MAC=$(.... command, then run the JSSGroups=... command). Forexample, my Mac is a member of the following groups:

'All Managed ClientsnCasper Admin StationsnCED PronFirefox 3.6.20nFull Office SuitenMac Pilot Group \- ISD OnlynManaged Clients \- OS 10.6.6 +nManaged Clients \- WHQnManaged Clients \- WHQ No 10.7/10.8nNon_Creative Workstations \- WHQnPilot GroupnPrint.WHQnSnow Leopard Client Desktops \- WHQnSoftwareUplift-FlashPlayer 11.5.502.149nSoftwareUplift-PrintWindow 4.1.5nSymantec Endpoint Protection 12.1nUnused Quark XPress 7n'

Then we grep out the known software that we maintain updates on. I have a smart group in Casper for each of these titles. Once I pull the memberships with the API call, if an update is needed, it should show here. So I grep for 'SoftwareUplift-<softwarename>' where <softwarename> is one of the titles I've defined above in 'NonSysCore=( 'SoftwareUplift-FlashPlayer' 'SoftwareUplift-Flip4Mac' 'SoftwareUplift-FontNuke' 'SoftwareUplift-PrintWindow' 'SoftwareUplift-MicrosoftOffice' 'SoftwareUplift-MicrosoftOutlook' )'

If there's a match, we call ```
jamf policy -trigger SoftwareUplift-<softwarename>
```. I use the same naming scheme for my smart groups as I do for my policy triggers. This way I only have to update one section in the code if we ever get a new software package that has to be updated on a regular basis. Just make sure the group names follow the same naming convention as the trigger names.

Like
SOLVED Posted: by jhbush1973

Andrew, I get the logic now. My other question is how do you handle the OS updates in the policy? Do the reboot options hold in Reboot tab of a policy or how do you use that or get the machine to reboot if that doesn't work.

Like
SOLVED Posted: by acdesigntech

Yes, the reboot options you set in Casper hold for software updates that require reboots. The scripts kills jamfHelper, not softwareupdate, so the HUD window thrown up by Casper while an update is running is killed once the update is finished, and the "you need to reboot" window will remain.

the reboot prompt is considered a different process than the jamfHelper you are showing to alert the user there's an update going on, so killing the jamfHelper PID will ONLY kill that window. if you issue a killall jamfHelper, you might also kill that reboot prompt.

so all you should have to do is set it to reboot only if a software update or package requires it.

Like
SOLVED Posted: by rsnediker

Hi All,

I've modified Lisa's script for our deployment. My goal was to keep the jamfHelper dialog boxes throughout the script, output easier to read and more verbose logs, and use the pretty software update icon. Here's some things to keep in mind about how I've accomplished these goals.

  1. The policy that runs the script executes four times per week, Tuesday, Thursday, Saturday and Sunday. On Tuesday and Thursday it does not execute between 7AM-6PM, and on Saturday and Sunday it can execute all day (this requires two policies). This ensures that the users do not get annoyed with the frequency and that updates do not occur during the workday.
  2. The runsoftwareupdate trigger is a policy that can be executed at any time through Self Service and is scoped to the group that has software updates available. This allows the user to run updates at their leisure if they desire.
  3. Think about modifying the Software Update line with your company's name to something like Pretendco Software Update to help user's see that these are internal updates.
  4. You may also want to have a line return after the contact your Help Desk line and add contact information for "Email (non-urgent): " and "Phone (urgent): "and

Hope this helps, and thanks to Lisa for making such a great script!

#!/usr/bin/perl -w

use strict;

my $AVAILABLEUPDATES="";
my $CHANCESTOUPDATE=6;
my $COUNTFILE='/etc/SUScount.txt';
my $UPDATECOUNT=0;

$AVAILABLEUPDATES=`/usr/sbin/softwareupdate --list`;
chomp $AVAILABLEUPDATES;

printf "> Available updates are %s \n\n", "$AVAILABLEUPDATES";

unless (-e $COUNTFILE){
    system "/bin/echo 0 > $COUNTFILE";
} 

$UPDATECOUNT=`/bin/cat $COUNTFILE`;
printf "> Current update count is $UPDATECOUNT";

# If available updates contains * there are updates available

if ($AVAILABLEUPDATES=~/\*/){

    printf "> There are updates available.\n";

    if ($AVAILABLEUPDATES=~/(restart)|(shut\sdown)/){

        printf "> Updates require a restart.\n";

        my $LOGGEDINUSER='';

        $LOGGEDINUSER=`/usr/bin/who | /usr/bin/grep console | /usr/bin/cut -d " " -f 1`;
        chomp $LOGGEDINUSER;

        printf "> Value of logged in user is $LOGGEDINUSER.\n";

        if ($LOGGEDINUSER=~/[a-zA-Z]/) {

            printf "> Checking count as there is a logged in user.\n";

            my $CHANCESLEFT=$CHANCESTOUPDATE - $UPDATECOUNT;
            printf "> Chances left is $CHANCESLEFT\n";     

            if ($CHANCESLEFT <= 0) {
                printf "> No chances left, installing updates and will reboot...\n";
                system '\'/Library/Application Support/JAMF/bin/jamfHelper.app/Contents/MacOS/jamfHelper\' -startlaunchd -windowType utility -icon \'/System/Library/CoreServices/Software Update.app/Contents/Resources/SoftwareUpdate.icns\' -heading "Clayco Software Update" -description "Updates are being installed. You may continue to work until prompted to restart." -button1 "Okay"';
                system "/usr/sbin/jamf policy -trigger runsoftwareupdate";
                system "/bin/echo 0 > $COUNTFILE";
                exit 0;
            }

            else {
                printf "> $CHANCESLEFT chances left, checking whether ok to restart.\n";
                my $ATTEMPT = $UPDATECOUNT + 1;
                my $FINAL = $CHANCESTOUPDATE + 1;

                my $COMMAND='\'/Library/Application Support/JAMF/bin/jamfHelper.app/Contents/MacOS/jamfHelper\' -startlaunchd -windowType utility -icon \'/System/Library/CoreServices/Software Update.app/Contents/Resources/SoftwareUpdate.icns\' -heading "Software Update" -description "Your workstation has software updates to install, and they require a restart. Would you like to install the updates now?

Please contact the Help Desk with ANY questions." -button1 "Yes" -button2 "Postpone" -cancelButton "2"';

                my $RESPONSE = "";
                $RESPONSE=system $COMMAND;

                if ($RESPONSE eq "0") {
                    printf " - User said YES to updates.\n";
                    system '\'/Library/Application Support/JAMF/bin/jamfHelper.app/Contents/MacOS/jamfHelper\' -startlaunchd -windowType utility -icon \'/System/Library/CoreServices/Software Update.app/Contents/Resources/SoftwareUpdate.icns\' -heading "Software Update" -description "Your updates are being installed. You may continue to work until prompted to restart." -button1 "Okay"';
                    printf " - Okay Button Pressed \n";
                    system "/usr/sbin/jamf policy -trigger runsoftwareupdate";
                    system "/bin/echo 0 > $COUNTFILE";
                    exit 0;
                } else {
                    printf " - User said NO to updates.\n";
                    system '\'/Library/Application Support/JAMF/bin/jamfHelper.app/Contents/MacOS/jamfHelper\' -startlaunchd -windowType utility -icon \'/System/Library/CoreServices/Software Update.app/Contents/Resources/SoftwareUpdate.icns\' -heading "Software Update" -description "Thank you. You may install updates any time using Self Service, which is located in your Applications folder." -button1 "Okay"';
                    $UPDATECOUNT=$UPDATECOUNT + 1;
                    system "/bin/echo $UPDATECOUNT > $COUNTFILE";
                    printf " - Okay Button Pressed\n> Update count is now $UPDATECOUNT\n";
                    printf "> Chances left is $CHANCESLEFT\n";
                    exit 0;
                }
            }
        } else {
            printf "> No logged in user. Okay to run updates.\n";
            system "/usr/sbin/jamf policy -trigger runsoftwareupdate";
            system "/bin/echo 0 > $COUNTFILE";
            exit 0;
        }
    } 
    else {
        printf "> Updates do not require a restart. Installing.\n";
        system "/usr/sbin/jamf policy -trigger runsoftwareupdate";
        system "/bin/echo 0 > $COUNTFILE";
        exit 0;
    }
}
else {
    printf "> There are no updates available. Exiting.\n";
    system "/bin/echo 0 > $COUNTFILE";
    exit 0;
}
exit 0;
Like
SOLVED Posted: by jenski

This is awesome, exactly what I was looking for!

I do have a question that makes me feel really silly, however.

Does the script give a time limit between every time the machine asks if you'd like to install updates (I didn't actually see anything like that)? Or is it based on a trigger in the policy? For instance, if I had the policy set to every 15 minutes & the user chose to postpone the updates, would it postpone for 15 minutes & then push again?

In my case, the policy that I've created for the script is currently running once a week, on an every 15 trigger. It only runs on Tuesdays after 11:30am. Will this mean that all day, every 15 minutes, the script will push to ALL of the machines, whether or not the updates have installed? I'm worried that our Sys guys will see this script hammering the network all day & get a little peeved at me.

---

Just kidding, I answered my own question! Created a smart group for users that need updates & pushed the script just to them!

Like
SOLVED Posted: by mm2270

Jen, if you have a policy set to run once a week as its frequency, even if the trigger is set to the every15, it will only run once a week. it just means that the trigger that would cause it to come up once every Tuesday sometime after 11:30am would be when the Mac does its regular every15 check in cycle. But it would not come up again that same day once it executes.
And yes, you would need to set that up within the policy that pushes this script out. i don't think any of the versions here have a timing hardcoded into the script (though I have not read thoroughly through each version posted here)

Last thing is, there are no silly questions around here.

Like
SOLVED Posted: by quedayone

Do any of the above scripts inform the user that they only have a certain number of declines? And or tell the user how many declines they have left?
I am going to use one of the scripts just not sure what one.

Wi11

Like
SOLVED Posted: by acdesigntech

Mine does. It gives the user 5 cancelations before installing automatically.

Like
SOLVED Posted: by jwojda

when I run rsnediker's version, i get prompted to install, and if I choose to install then it installs, but never seems to reboot. However, because it still has the reboot install as needs to be installed, the jamfhelper continues to prompt. It's sat there for two hours and never rebooted.

Like
SOLVED Posted: by clifhirtle

Andrew, I'd like to use your script merely due to being more familiar with shell. Random question: how are you pushing non-Apple updates/install through the software update mechanism? Is this based off Munki or some other means of pushing down Office, etc al?

Like
SOLVED Posted: by acdesigntech

We have a finite number of non-apple software titles that we maintain, so I create smart groups based on a computer not having the updated version of the software. The update script first checks for memberships in any of those update groups, and if the computer is a member it runs a policy to update the software.

The policy triggers have the same names as the smart groups, so I can loop through the group names, and if a positive match is found, trigger the correct policy with the loop variable (which is the current group name). This way I just need to maintain the criteria of the smart group, and make sure I keep the correct packages/scripts in the policy. Then I run the update job, and Casper takes care of the rest.

See my final version of the script posted above, on 1/30/13

Like
SOLVED Posted: by clifhirtle

Thanks Andrew. I always defined my smart groups as identical to my policies for a specific update version like "Needs Flash 11.7.700.169" but I suppose one could also merely make the group a moving target of the latest version, so long as the policy that installs Flash 11.7.700.169 is detached from the group's scope if/when it is updated to look for a newer Flash version.

Like
SOLVED Posted: by acdesigntech

that's exactly what I do. I make the policy something like "SoftwareUpdate-FlashPlayer 11.7.700.169." Then I grep for group membership on "SoftwareUpdate-FlashPlayer," and if there's a match, then I trigger the "SoftwareUpdate-FlashPlayer 11.7.700.169" policy with the same name that I grepped for (SoftwareUpdate-FlashPlayer).

This way I keep it generic as far as policies go (and therefore easily repeatable without much maintenance), but on reports, it shows the exact version Flash Player the computer will be upgraded to. It's a nice feature of grep to work on substrings that lets me do this.

Like
SOLVED Posted: by JPDyson

I thought I'd share a spin on this that my colleague and I have started using. It combines a voluntary patch period (run from Self Service) with a "politely forced" patch period that's run from a typical policy.

We have a group of policies called the "patchme" policies, each of which are targeted by smart groups (like "Needs Flash 11.7.700.169"), set to ongoing, but only triggered by the manual trigger "patchme". At present, we have about a dozen of these for our core application stack, plus one for Apple SWU's, and finally one that performs a Recon (ordered by naming the policies with alphabetical prefixes). Oh, and for Office, that policy has a couple of additional notifications that are displayed to the user asking them not to use Safari or any Office apps (and another alert when the patch is finished).

These "patchme" policies are called by one of two other policies. The first is in Self Service, and it really only does two things: run the script "patchmeVoluntary" and reboot if a package or SWU requires. That script will call the patchme policies and create a package receipt for this month's patches (the name being passed to the script as $4).

#!/bin/sh

# Have we run this already?
if [ -f /Library/Application\ Support/JAMF/Receipts/$4 ]; then
    echo "Patches already run"
    /Library/Application\ Support/JAMF/bin/jamfHelper.app/Contents/MacOS/jamfHelper -windowType hud -icon '/System/Library/CoreServices/Software Update.app/Contents/Resources/SoftwareUpdate.icns' -heading "Updates already installed this month" -description "Thank you for being proactive about keeping your Mac up to date. Check back later for more updates." -button1 "OK" -defaultButton "1" -timeout 30
    exit 2
fi

# Heads Up
/Library/Application\ Support/JAMF/bin/jamfHelper.app/Contents/MacOS/jamfHelper -windowType hud -windowPosition ur -icon /System/Library/CoreServices/Installer.app/Contents/Resources/Installer.icns -heading 'EI is installing updates to your Mac' -description 'Please do not turn off this computer. It will reboot when updates are completed.' > /dev/null 2>&1 &

# Run the update policy
/usr/sbin/jamf policy -trigger patchme
touch /Library/Application\ Support/JAMF/Receipts/$4

killall -9 jamfHelper
/Library/Application\ Support/JAMF/bin/jamfHelper.app/Contents/MacOS/jamfHelper -windowType hud -icon /System/Library/CoreServices/Installer.app/Contents/Resources/Installer.icns -heading 'Updates Complete' -description 'If a reboot is required, it will be performed now.' -button1 "OK" -defaultButton "1" -timeout 30 
exit 0

A couple of remarks: Yes, I know I'm not using the PID, but I used to... I had found in some cases that it was ineffectual in actually killing the jamfHelper process (and when I used to use a full screen window, man that sucked). For my purposes, killall works just fine. Timers are on the windows so that they don't hold up a policy indefinitely.

With the second, "polite forced" policy, we use the script "patchmePrompt". Hat tip to Andrew; I basically poached this from his example and adapted it for the above policy scheme.

#!/bin/sh

# Have we run this already?
if [ -f /Library/Application\ Support/JAMF/Receipts/$4 ]; then
    echo "Patches already run"
    exit 2
fi

# Let's make sure we've got the timer file
if [ ! -e /Library/Application\ Support/JAMF/.SoftwareUpdateTimer.txt ]; then
    echo "5" > /Library/Application\ Support/JAMF/.SoftwareUpdateTimer.txt
fi

LoggedInUser=`who | grep console | awk '{print $1}'`
Timer=`cat /Library/Application\ Support/JAMF/.SoftwareUpdateTimer.txt`

fRunUpdates ()
{
    echo "5" > /Library/Application\ Support/JAMF/.SoftwareUpdateTimer.txt

    /Library/Application\ Support/JAMF/bin/jamfHelper.app/Contents/MacOS/jamfHelper -windowType hud -windowPosition ur -icon /System/Library/CoreServices/Installer.app/Contents/Resources/Installer.icns -heading 'EI is installing updates to your Mac' -description 'Please do not turn off this computer. It will reboot when updates are completed.' > /dev/null 2>&1 &

    # Run the update policy
    /usr/sbin/jamf policy -trigger patchme
    touch /Library/Application\ Support/JAMF/Receipts/$4

    killall -9 jamfHelper
    /Library/Application\ Support/JAMF/bin/jamfHelper.app/Contents/MacOS/jamfHelper -windowType hud -icon /System/Library/CoreServices/Installer.app/Contents/Resources/Installer.icns -heading 'Updates Complete' -description 'If a reboot is required, it will be performed now.' -button1 "OK" -defaultButton "1" -timeout 30 
    exit 0
}

# If nobody's home, fire away. Else, prompt (assuming they haven't delayed too many times)
if [ "$LoggedInUser" == "" ]; then
    fRunUpdates
else
    if [ $Timer -gt 0 ]; then
        HELPER=`/Library/Application\ Support/JAMF/bin/jamfHelper.app/Contents/MacOS/jamfHelper -windowType hud -windowPosition ur -icon /System/Library/CoreServices/Installer.app/Contents/Resources/Installer.icns -heading "Software Updates are available for your Mac" -description "If you would like to install updates now, click OK. If you would not like to install updates now, click Cancel. You may choose to not install updates $Timer more time(s) before this computer will forcibly install them. A reboot will be required." -button1 "OK" -button2 "Cancel" -defaultButton "2" -timeout 300 -countdown -startlaunchd`

        echo "jamf helper result was $HELPER";

        if [ "$HELPER" == "0" ]; then
            fRunUpdates
        else
            let CurrTimer=$Timer-1
            echo "user chose No"
            echo "$CurrTimer" > /Library/Application\ Support/JAMF/.SoftwareUpdateTimer.txt
            exit 1
        fi
    fi
fi

# If Timer is already 0, run the updates automatically, the user has been warned!
if [ $Timer -eq 0 ]; then
    fRunUpdates
fi

Reboots are dependent upon the packages being marked "requires reboot". A new cycle begins when we rename the package receipt ($4).

Like
SOLVED Posted: by clifhirtle

@Andrew Finally got around to setting up and trying your script/setup. Unfortunately I'm running into the same problem I hit earlier with Mike's policy that calls a policy (https://jamfnation.jamfsoftware.com/discussion.html?id=7184) \- when one policy has the DP already mounted over SMB, the other child policy errors out because it cannot mount an already mounted DP share and I'd rather not rely on a client-side script (as recommended in thread linked above). May have to just rely on the script for standard Apple SW Updates for now, leaving the other 3rd party updates to separate policy triggers.

Like
SOLVED Posted: by JPDyson

Clif, I see that (inconsistently) as well. This is the main reason I want to go to http distribution points.

Like
SOLVED Posted: by acdesigntech

i've actually set up a smart group based on an EA that checks group membership in any of the groups above as well as running a 'softwareupdate -l'. If any of the checks come back positive, the "Updates Available" EA get set to yes.

Then, rather than triggering the main update policy on everyone, I trigger it only on computers that have a "Yes" for this EA. I make the policy ongoing, and voila! automatic until a computer no longer reports a "Yes" on the Updates Available EA. Since inventory is updated at the end of the main policy, this EA should remain fairly up-to-date.

I push everything over AFP right now, which does allow the same share to be mounted more than once.

Like
SOLVED Posted: by clifhirtle

Here's a workaround for those of us stuck on SMB shares and "cannot mount distribution point" errors but still want the advantages of hierarchal policy scoping via Mike's script. Sort of takes Andrew's model and tweaks to meet need of 1x/policy/time:

  • Smart group: set to EA criteria you want to check, say "SoftwareUpdate-MSOffice"
  • Policy #1: install/run your desired package/script, ongoing execution, but w/manual trigger, scoped to smart group above (ie: criteria match)
  • Policy #2: scoped to machine group you're targeting (say: Phase1-Testing), executing 1x/computer, and running command to trigger policy #1 "/usr/sbin/jamf policy -trigger SoftwareUpdate-MSOffice" in the Advanced tab.

This has same effect of permitting hierarchal policy scoping, with policy 1 doing heavy work only across machines that need it and only called by policy #2, which is duplicated and set with different machine groups and date/time limitations to ensure phased rollouts.

Not the simplest approach, but does seem to be a short-term workaround to the SMB share errors until Casper 9 comes out and makes all this unnecessary.

Like
SOLVED Posted: by joemamasmac

Hello All,

I decided to try one of the scripts from this thread in my environment and I am running into a problem. Users on 10.7 have no issues with the script and everything seems to work just fine. People running 10.8 though, get the pop up saying there are updates and asking them to install. If they hit install, nothing happens. This works in 10.7, but I can't figure out why not in 10.8. Any ideas?

#!/usr/bin/perl -w

use strict;

my $AVAILABLEUPDATES="";
my $CHANCESTOUPDATE=3;
my $COUNTFILE='/etc/SUScount.txt';
my $UPDATECOUNT=0;


$AVAILABLEUPDATES=`/usr/sbin/softwareupdate --list`;
chomp $AVAILABLEUPDATES;

printf "available updates is %s \n\n", "$AVAILABLEUPDATES";

unless (-e $COUNTFILE){
system "/bin/echo 0 > $COUNTFILE";
} 

$UPDATECOUNT=`/bin/cat $COUNTFILE`;
printf "update count is $UPDATECOUNT\n\n";

# If available updates contains * there are updates available

if ($AVAILABLEUPDATES=~/\*/){

printf "there are updates available\n";

if ($AVAILABLEUPDATES=~/(restart)|(shut\sdown)/){

printf "updates need a restart\n";

my $LOGGEDINUSER='';

$LOGGEDINUSER=`/usr/bin/who | /usr/bin/grep console | /usr/bin/cut -d " " -f 1`;
chomp $LOGGEDINUSER;

printf "value of logged in user is $LOGGEDINUSER..\n";

if ($LOGGEDINUSER=~/[a-zA-Z0-9]*/) {

printf "as there is a logged in user checking count\n";

my $CHANCESLEFT=$CHANCESTOUPDATE - $UPDATECOUNT;
printf "Chances left is $CHANCESLEFT\n"; 

if ($CHANCESLEFT <= 0) {
printf "No chances left installing updates and will reboot..\n";
system '/usr/sbin/jamf displayMessage -message "Your computer is installing updates and will reboot shortly."';
system "/usr/sbin/jamf policy -trigger runsoftwareupdate";
system "/bin/echo 0 > $COUNTFILE";
exit 0;
}

else {
printf "$CHANCESLEFT chances left... checking whether ok to restart\n";
my $ATTEMPT = $UPDATECOUNT + 1;
my $FINAL = $CHANCESTOUPDATE + 1;

my $COMMAND= "\'/Library/Application Support/JAMF/bin/jamfHelper.app/Contents/MacOS/jamfHelper\' -startlaunchd -windowType utility -icon \'/Library/Application Support/JAMF/bin/jamfHelper.app/Contents/Resources/Message.png\' -heading \"Software Updates are Available for Your Computer\" -description \"This is installation attempt $ATTEMPT of $CHANCESTOUPDATE. On attempt $FINAL, updates will install automatically and your machine will restart.\" -button1 \"Yes\" -button2 \"Defer\" -cancelButton \"2\"";

my $RESPONSE = "";
$RESPONSE=system $COMMAND;

if ($RESPONSE eq "0") {
printf "\nUser said YES to Updates\n";
system "/usr/sbin/jamf policy -trigger runsoftwareupdate";
system "/bin/echo 0 > $COUNTFILE";
exit 0;
} else {
printf "\nUser said NO to Updates update count is $UPDATECOUNT\n";
$UPDATECOUNT=$UPDATECOUNT + 1;
system "/bin/echo $UPDATECOUNT > $COUNTFILE";
printf "\nUpdate count is now $UPDATECOUNT\n";
exit 0;
}
}
} else {
printf "no logged in user so ok to run updates\n";
system "/usr/sbin/jamf policy -trigger runsoftwareupdate";
system "/bin/echo 0 > $COUNTFILE";
exit 0;
}
} 
else {
printf "no restart required\n";
system "/usr/sbin/jamf policy -trigger runsoftwareupdate";
system "/bin/echo 0 > $COUNTFILE";
exit 0;
}
}
else {
printf "there are no updates available\n";
system "/bin/echo 0 > $COUNTFILE";
exit 0;
}
exit 0;

Thanks for any suggestions.

Joe

Like
SOLVED Posted: by lisacherie

@joemamasmac

A while ago the check box to install softwareupdates in a policy was not working, i changed the triggered policy at the time to instead run command: softwareupdate --install --all (I have since changed it back to the checkbox).

check your casper install is fairly current and try updating or modify your called policy when there are updates available.

Like
SOLVED Posted: by JPDyson

@clif, Yes \- If the "root" policy that kicks off the "other" policies can avoid mounting a distribution point, the subsequent policies are not affected. The problem is that I want the root policy to do a little bit more for me, such as warning the user that patches are coming, giving them a window to delay said patches, and so on.

A work-around here is to package the "caller" script and deploy it in your environment so that it's local on the machine. Have your root policy call the trigger like you say, and make the first policy a posture-check or even blindly replace with the latest client-side updater script. Said client-side script can display your warning windows and the like without a DP being mounted, and is then free to call subsequent policies which mount the DP.

Like
SOLVED Posted: by maik.sanftenberg

I wonder if there is a way to use this script as well to check other updates?
We have multiple policies that are used in Self Service and are scoped by Smart Groups to Update Java, Flash e.g.

We have one Policy that we uses to check if any of this updates need to be installed. We use

sudo jamf policy -id 259; sudo jamf policy -id 175;...
to do this...

Is there a way to modify the script in a way that it checks first if the users is part of any of this groups and provide a pop-up only if he is part of a Smart Group?

Thanks,
Maik

Like
SOLVED Posted: by clifhirtle

Circling back on this one, anyone seen issues in using the shell variants of these scripts that simply never dismiss the "updates are running" dialog? I'm using a slightly tweaked version of Andrew's script here and seeing a bunch of clients that are successfully installing updates but not actually dismissing the dialog when done. Assuming this is related back to failure in correctly ID'ing the jamfHelper PID routine below, but any pointers or personal experience appreciated.

## We'll need the pid of jamfHelper to kill it once the updates are complete
    JHPID=`echo "$!"`

/usr/sbin/jamf policy -trigger SoftwareUpdate-Apple-OSX &

## Get the Process ID of the last command run in the background ($!) and wait for it to complete (wait)
SUPID=`echo "$!"`
wait $SUPID

## kill the jamfHelper. If a restart is needed, the user will be prompted. If not the hud will just go away 
kill -s KILL $JHPID
Like
SOLVED Posted: by stevewood

You can try killing jamfHelper with the following command, instead of using the PID. That should at least tell you if it is a problem with the PID process:

killall jamfHelper
Like
SOLVED Posted: by mm2270

In my experience, getting the process ID of a jamfHelper call sent to the background with echo $! is not so accurate. I often see a one digit disparity between that and using something like:

ps axc | awk '/jamfHelper/{print $1}'

For example, if I call up a jamfHelper window and send it to the background, then immediately do echo $!, it may report a PID of 97823. If I use the above ps command, it reports 97824. Trying to kill pid 97823 fails, but killing 97824 works. So you may be seeing the same thing here. Use either killall jamfHelper like @stevewood suggests, or try a targeted kill approach with:

kill -9 $(ps axc | awk '/jamfHelper$/{print $1}')
Like
SOLVED Posted: by clifhirtle

Thanks guys. My going with the killall option, are we in any danger of killing the subsequent (and more user-critical) default JAMF reboot dialog?

Like
SOLVED Posted: by clifhirtle

double post

Like
SOLVED Posted: by mm2270

As long as your killall command comes before sending up the reboot jamfHelper dialog, you should be OK. Order in the script will be important.

Edit: Sorry kind of misread. If you're using the built in Casper Suite reboot dialog, I actually don't know that that uses jamfHelper. Been a long time since I've used it myself, so I'm not sure. Even if it does, again, provided it comes after your killall command, I think it should be alright.

Like
SOLVED Posted: by clifhirtle

As far as I can tell, any of these scripts would simply hand off the presentation of the final dialog back to Casper. And given that this kill/killall command is the last command in the fRunUpdates routine it seems like a pretty close call in terms of kill then hand-off. I'll give this a shot on a few test boxes here and see what happens.

Like
SOLVED Posted: by mm2270

Clif, if I'm not mistaken, the script has to close out before it can hand off to the final process in the policy to send up the reboot message, meaning, the script shouldn't be running at that point. so you really should be OK.

But yes, of course test it and see what happens. I'd be curious to hear if it actually ends up killing the final dialog. I really doubt it though.

Like
SOLVED Posted: by jhbush1973

Clif, would you mind reposting your final script for comparison to what others have posted?

Like
SOLVED Posted: by clifhirtle

Sure thing. Here's what I've got, largely just a rehashing of Andrew's earlier script, with bit more user-friendly dialoging and integration with Casper Admin variables.

Like others, this update runs weekly to check for updates / present user deferment options, then just hands off to the SoftwareUpdate-Apple-OSX policy scoped to machines needing SW updates that runs the standard Casper install all updates function.

#!/bin/sh

## Shell script for managed Apple Software Updates
## courtesy acdesigntech @ https://jamfnation.jamfsoftware.com/discussion.html?id=5404
## updated by C.Hirtle on 8/19/13

#### Read in the parameters
mountPoint=$1
computerName=$2
username=$3
postpones=$4            # sets number of times user can postpone updates before forcing down
NonSysCore1=$5          # sets policy name of non system update group
NonSysCore2=$6          # sets policy name of non system update group
NonSysCore3=$7          # sets policy name of non system update group
NonSysCore4=$8          # sets policy name of non system update group
NonSysCore5=$9          # sets policy name of non system update group


fRunUpdates ()
{

    ## Once the user OKs the updates or they run automatically, reset the timer to value set above 
    echo "$postpones" > /Library/Application\ Support/JAMF/.SoftwareUpdateTimer.txt

    /Library/Application\ Support/JAMF/bin/jamfHelper.app/Contents/MacOS/jamfHelper -windowType hud -lockhud -heading 'Software Updates are Now Installing' -description 'Software updates are now being installed on your Mac. You may continue to use your Mac or dismiss this dialog. Updates should take no longer than 15-30 minutes depending on how many updates your Mac needs. If you see this screen for more than 30 minutes, contact the Help Desk at helpdesk@pretendco.com. Do not turn off your Mac. This message will go away once updates are complete.' -alignDescription justified -icon /System/Library/CoreServices/Software\ Update.app/Contents/Resources/SoftwareUpdate.icns > /dev/null 2>&1 &

    ## We'll need the pid of jamfHelper to kill it once the updates are complete
    JHPID=`echo "$!"`

    /usr/sbin/jamf policy -trigger SoftwareUpdate-Apple-OSX &
    ## Get the Process ID of the last command run in the background ($!) and wait for it to complete (wait)
    SUPID=`echo "$!"`
    wait $SUPID

    ## kill the jamfHelper. If a restart is needed, the user will be prompted. If not the hud will just go away 
    kill -s KILL $JHPID
    killall jamfHelper
    exit 0
}


######### Set variables for the script ############

########## Get the group membership for the client #####################
## Get MAC Address using networksetup
MAC=$( networksetup -getmacaddress en0 | awk '{ print $3 }' | sed 's/:/./g' )

## Use the JSS API to get the Mac's group memberships
JSSGroups=$( curl -s -u svc_apiread:apireadpass https://casper.pretendco.com:8443/JSSResource/computers/macaddress/$MAC \
| xpath //computer/groups_accounts/computer_group_memberships[1] \
| sed -e 's/<computer_group_memberships>//g;s/<\/computer_group_memberships>//g;s/<group>//g;s/<\/group>/\n/g' )

## Get the timer value
Timer=`cat /Library/Application\ Support/JAMF/.SoftwareUpdateTimer.txt`

## Get the currently logged in user, if any. Also check for updates that require a restart and ones that do not.
LoggedInUser=`who | grep console | awk '{print $1}'`
UpdatesNoRestart=`softwareupdate -l | grep recommended | grep -v restart`
RestartRequired=`softwareupdate -l | grep restart | grep -v '\*' | cut -d , -f 1`

################ End Variable Set ################

## Use echo and grep to find known-core (non system) software update groups. If these groups are found, run these installers silently since no restarts are required for these updates. Use an array to see which updates we take account of. The names of the array elements are also trigger names for each update. This way when there's a new software package to keep updated, we add the trigger name into the array, and the update policy to the JSS. Casper does the rest
NonSysCore=( '$NonSysCore1' '$NonSysCore2' '$NonSysCore3' '$NonSysCore4' '$NonSysCore5')

for (( i = 0; i < ${#NonSysCore[@]}; i++ ))
do
    CheckUpdate=`echo "$JSSGroups" | grep "${NonSysCore[$i]}"`
    if [ "$CheckUpdate" != "" ]; then
        jamf policy -trigger "${NonSysCore[$i]}"
    fi
done

## If there are no system updates, quit
if [ "$UpdatesNoRestart" == "" -a "$RestartRequired" == "" ]; then
    echo "No updates at this time"
    exit 0
fi

## If we get to this point and beyond, there are updates. Check to see if there is a timer file on the Mac. This file tells the script how many more times it is allowed to be canceled by the user before forcing updates to install
if [ ! -e /Library/Application\ Support/JAMF/.SoftwareUpdateTimer.txt ]; then
    echo "$postpones" > /Library/Application\ Support/JAMF/.SoftwareUpdateTimer.txt
fi

## if there is no one logged in, just run the updates
if [ "$LoggedInUser" == "" ]; then
    /usr/sbin/jamf policy -trigger SoftwareUpdate-Apple-OSX
else
    if [ $Timer -gt 0 ]; then
            ## If someone is logged in and they have not canceled X times already, prompt them to install updates that require a restart and state how many more times they can press 'Postpone' before updates run automatically.
        if [ "$RestartRequired" != "" ]; then
            HELPER=`/Library/Application\ Support/JAMF/bin/jamfHelper.app/Contents/MacOS/jamfHelper -windowType utility -icon /System/Library/CoreServices/Software\ Update.app/Contents/Resources/SoftwareUpdate.icns -heading "Software Updates Available" -description "Software updates are available that require you to restart your Mac. If you want to install these updates now, click Install. To postpone installing updates to a later time, click Postpone. Updates requiring restart are:

$RestartRequired

You may choose not to install updates $Timer more times before this computer will automatically install them." -button1 "Install" -button2 "Postpone" -cancelButton "2"`
            echo "jamf helper result was $HELPER";
            ## If they click Install Updates then run the updates
            if [ "$HELPER" == "0" ]; then
                fRunUpdates
            else
            ## If no, then reduce the timer by 1. The script will run again the next day 
                let CurrTimer=$Timer-1
                echo "user chose No"
                echo "$CurrTimer" > /Library/Application\ Support/JAMF/.SoftwareUpdateTimer.txt
                exit 1
            fi
        fi
    ## If Timer is already 0, run the updates automatically, the user has been warned!
    else
        fRunUpdates
    fi
fi

## Install updates that do not require a restart
if [ "$UpdatesNoRestart" != "" ]; then
    /usr/sbin/jamf policy -trigger SoftwareUpdate-Apple-OSX 
fi
Like
SOLVED Posted: by acdesigntech

Cliff, I especially like the change to display what updates want a restart. I never quite got that right, I'm going to test and incorporate that in the next round of updates :)

I've only seen jamfhelper not get killed once during the frunupdates routine. My restart is specified as immediately, so that might be why it hasn't come up before: by the time someone gets around to complaining the Mac restarts :)

Like
SOLVED Posted: by clifhirtle

This thread has been invaluable in getting a sustainable, scalable, low-touch software update mechanism in place for us so glad to contribute anyway I can.

One question: is the "lockhud" user dialog giving notice that software updates are installing designed to come up with any/all updates? Had originally thought it was presented for reboot-only, but seems like I'm seeing on many others too. I'm always trying to err on the side of minimal user intrusion, but I could see the logic of why you might want to FYI users to not reboot if updates are installing.

Like
SOLVED Posted: by acdesigntech

The hud dialog about updates are being run SHOULD only come up in the case of updates requiring a restart \- whether that's because the user allowed it or the timer reached zero and the updates are forced.

I would think in order to specify if a user needs to restart or not you could pass a parameter to that routine, and have a variable in the jamfhelper dialog that says restart or not based on the value of that passed parameter.

Like
SOLVED Posted: by loceee

Hey gang,

I've been pretty quiet here, but have been cooking up something that I feel fills some gaps in Casper.

I call it munki-for-jamf... a.k.a. junki.

I wanted to emulated the munki end-user experience via Casper and deliver Apple and Casper updates via the same UI. Allow users to be somewhat flexible with installs (defer x number of times), and also integrate with all existing Casper workflows and methodologies.

I am finishing a bootstrap functionality for freshly imaged / deployed computers at the moment and after some further testing I will release it into the wild.

Here's a rough overview... https://interpublic.box.com/s/ri59s7gttpy5ua5h8ybi

It's currently deploying Apple Software updates across 13 sites, and 500+ Macs within our group of companies.

Like
SOLVED Posted: by Kumarasinghe

@loceee
Thanks.

Like
SOLVED Posted: by golbiga

@loceee looking forward to testing this! Are you going to be at JNUC?

Like
SOLVED Posted: by jwojda

I'd be interested in checking this out and compare to Casper's 9.x method.
Does it have a max deferment, meaning you can defer it up to X days before it's forced?

Like
SOLVED Posted: by loceee

@golbiga \- unfortunately I am on the other side of the world and am expecting a new addition to my family any minute. I can't make it... but some of my co-workers will be there. Maybe next time.

@jwojda \- Yes, it has a defer count that can be set. Defaults to 10x. If you are running on a daily trigger, that's 10 days. It's just stored in a plist, change it with defaults for specific computers with a policy or edit it in the script.

Here's a sample log out from the jss...

Running script 0junki.sh...
Script exit code: 1
Script result: junki --promptinstall: swupdate pkgs waiting to be installed
junki --promptinstall: -------------------------------------
junki --promptinstall: MacBookProEFIUpdate2.7-2.7
junki --promptinstall: MacBookProSMCUpdate1.7-1.7
junki --promptinstall: RAWCameraUpdate4.09-4.09
junki --promptinstall: ThunderboltFirmwareUpdate1.2-1.2
junki --promptinstall: iTunesXPatch-11.1.0
junki --promptinstall: OSXUpd10.8.5-10.8.5
junki --promptinstall: -------------------------------------
junki --promptinstall: user selected install later
junki --promptinstall: USERNOTIFY:: Installion Defered, You can defer the installation 3 more times
junki --promptinstall: jamf is running a recon...
Retrieving inventory preferences from https://jss.interpublic.com.au:8443/...
Finding extension attributes...
Locating hard drive information...
Locating hardware information (Mac OS X 10.8.4)...
Like
SOLVED Posted: by jennifer

Has anyone been able to run the jamfhelper when no one is logged in?

I'm trying to run software update after imaging and/or when users have logged off for the day, but I want the splash screen to come up to prevent someone from logging in while the updates are in progress.

Just a side note, lots of thanks for the scripts already posted in this thread. They've been really helpful!

Like
SOLVED Posted: by mm2270

@jennifer_unger, its not that easy to do because of security features in the OS, but you can look through this older thread. There are some solutions posted, including a LaunchAgent that calls a script to do a full screen jamfHelper message.

https://jamfnation.jamfsoftware.com/discussion.html?id=24

Like
SOLVED Posted: by maik.sanftenberg

@ Lachlan is there any place where I can get junki shell script/s?
I really would like to look into this as this is what we are looking for since quite a while.

Thanks,
Maik

Like
SOLVED Posted: by loceee

Hi @maik.sanftenberg,

I will release in a month or two once I've finalised testing and tidied things up (I am also about to go on paternity leave for a couple of weeks \- sorry!). It's almost feature complete now, I only need to add a --priority / --forced mode so that Casper pkgs that need to be prioritised / installed by themselves are handled (ie. OSUpgrade pkgs from Greg Neagle's awesome createOSXinstallPkg) then it's testing and tidy up.

As a teaser I have got the "bootstrap" mode up and running well. In the same way munki bootstraps, the Macs boot, lock the loginwindow, check, then install all updates, reboot as necessary, then loop again until completely patched.

Screenshots... just using jamfhelper -fs to give some UI feedback.

https://interpublic.box.com/s/jg9l7mx3on2kw6sipc74

Like
SOLVED Posted: by haircut

@loceee Any chance of taking a look at the code as it stands?

Like
SOLVED Posted: by loceee

@bmwarren][/url \- soon I promise!

I just need to make the logout mechanism a little more robust and quash a couple of final bugs.

I've update my presentation to convey a couple of changes \- https://interpublic.box.com/s/yj4iudzllxizs3z9r03a

and improved the UI feedback during bootstrap \- https://interpublic.box.com/s/jg9l7mx3on2kw6sipc74

Like
SOLVED Posted: by loceee

Good news is it's now feature complete for a testing release. I will finalise my internal testing and get some docs together. I hope to have something to share by the end of next week.

Here's a shot of the new --osupgrade mode that is designed to handle unattended installers made by Greg Neagle's awesome createOSXInstall.

https://interpublic.box.com/s/4buc4y32o9pxol0hzifr

Like
SOLVED Posted: by clifhirtle

Thanks for putting this together Lachlan. Looks similar to what we hacked together off Andrew and others' custom scripts (see below), but the overall problem at hand remains one of my biggest frustrations with the Casper suite: minimal OOTB user dialog options. There have been times when all I want to do is present a multiple-choice dialogue to a user and end up spending hours pouring through old scripts to hodgepodge something together that will fit the need. SW updates are something that are such a core element of ensuring a secure endpoint that I really hate rolling the dice with custom anything every time I make change to our workflow. More stuff like this is a good thing.

external image link

Like
SOLVED Posted: by loceee

That looks pretty neato @clifhirtle][/url][/url !

Part of the junki magic is that Casper deployed software would also appear in the dialog.

Sorry to hijack this thread, but there is a bit of an issue with junki output and Casper 9. If you could all vote this up it would help with usability on the JSS v 9.

https://jamfnation.jamfsoftware.com/featureRequest.html?id=1670

Like
SOLVED Posted: by haircut

@loceee Yes, no part of JSS v9.x respects line breaks of any type. I'd call it a defect. I had a brief support case and was told

Thanks for contacting us today about line breaks in Self Service. We are currently looking to correct that issue in an upcoming release but I'm not sure when that will occur.

Unfortunate; it breaks functionality and makes things looks messy and unprofessional, for instance Self Service descriptions can only be long run-ons. I definitely up voted your feature request!

Like
SOLVED Posted: by mikedodge04

Hey @loceee,
I wanted to check out your code but it seems that you box link died. Would your consider hosting on github? or re share the box link?

Like
SOLVED Posted: by ssmurphy

hello @loceee,

Any updates on your software? Would like to see if it would fit our users needs.

Thanks,

Like
SOLVED Posted: by ssmurphy

Hello @loceee

Ping you again on any updates to the junki script you had talked about last year. Your method looked like the best method of getting updates to our clients systems. Any info on where this stands would be great. Even if its to just let the JAMF Community know that it may never see the light of day.

Thanks of your time.

Like
SOLVED Posted: by clifhirtle

Looking back at the preso you have posted above Lachlan, is it correct to assume the junki script prompts for all updates/installs in the same dialog (versus each individual update selectable like Munki's managed software install dialog)?

The way Munki permits individualized installers is really the "special sauce" I would love to see implemented in the Casper suite.

The script I am using currently for SW updates (inspired off great work @acdesigntech and others above have contributed) already allows for integration of 3rd party installers/updates, specifying \# of postpones, etc so the really big missing piece just remains the lack of user opt-in/out for specific updates similar to the ASU/Munki UI.

Like
SOLVED Posted: by mm2270

@clifhirtle, would something like this be more what you;re looking for?

external image link

In case you're wondering that's simply cocoaDialog using the checkbox option available in the 3.0 beta releases. The items listed in it weren't hardcoded, but generated dynamically by running a softwareupdate -l command and parsing the output, then dropping it all into an array that cocoaDialog can use.

Although this is only a start, I'm pretty sure I could put something together that would take any checked items and correctly install only those updates and skip the others. I've done some similar things recently, so I can take what I've already scripted and adapt it to this purpose.

If that's something you might be interested in, let me know and I'll see what I can put together.

Like
SOLVED Posted: by loceee

Sorry all, been crazy with every spare moment taken up outside of work spent on selling, finding, buying a new home \+ new baby and endless illness going through home! That and one of our guys has gone and left to take a role with JAMF so I've been picking up more overflow support stuff (naughty Rusty) :)

Finally all settling down now, moving next weekend and I might have some more time to nail down the last couple of things. I will also need clearance from legal to release it, so thanks for your patience!

I like the optional installs @mm2270][/url !

As for junki's design, it doesn't allow optional installations as such, it's really ONLY about patching stuff. I didn't want to re-invent the bits of Casper that are already are great.

Here are my thoughts around this is, we have 3 solutions for deploying software with Casper, policies (silently), policies (self service \- hence optional installs) and via Casper Imaging.

The workflows we are using (and I will be recommending in my documentation) will be as follows.

\- New Mac
\- Casper Imaging (thin or erased and install a vanilla OSX unbooted with an overload of apps, and configuration via scripts, MCX, profiles \- nothing should be "baked in" any base images). -- (Casper Imaging and configurations is a supported, and quite easy way to deploy initial software on to a computer. I DO think it should be renamed to "Casper Deploy" as you don't have image with it.)
\- Reboot
\- Casper Imaging does it firstboot script (which includes junki bootstrap enable script)
\- Reboot
\- junki bootstrap does a recon, update install, apple software update loop until all updates are applied \- this will compensate for any version drift in your computer configurations.

From then on, junki patching is run on a regular schedule (daily if you like) and machines are patched going forward, but allowing optional installs is not part of this workflow. If a user wants Firefox installed, they perform this via Self Service... since junki scopes update patches via smart groups, once Firefox is installed, it falls in the smart group and junki will then perform updates on it... but if it's not present it won't be installed via junki, nor am I planning on making any installations optional (apart from deferring).

A new feature I've added since last chat is an emulation of the munki "mainfests".. so you can scope testing / dev / production patches via JSS groups. eg. you add a few power users to your junkiBeta group, put all your testing Macs into the junkiDev group.

I know some people run a completely separate JSS for dev, which I don't really like when we are talking about patch deployment. It makes sense to test other things in Casper, but moving a patch (policy, pkg and smart group) between environments is cumbersome, (I did see Bryson at JAMF is working on something to make it easier), but I don't think it's the right approach myself. Using this method you have visibility of patches in testing in a single JSS, and even the ability to have a number of "testing levels" eg. dev/alpha/beta/powerusers/itstaff/brave/suckers/production.

on patching runs junki does the following,

queries the JSS to see if the computer is in junkiBeta, junkiDev group.
\- if so, it fires an update-dev / update-beta trigger, you can then attach your "beta" version of the patch via this policy. Production machines that aren't in special groups will just fire the "update" trigger.
\- it also dynamically re-writes the apple software update catalog if you are using reposado / netsus... so you can also have Macs in the junkiBeta group get pointed to http://yoursoftwareupdateserver.domain:8088/content/catalogs/others/index-$osxversion.merged-1_beta.sucatalog based on JSS group membership!

Super cool, and it works great! You can setup dev / beta / production branches for your Apple Software Updates as well!

Now for release, I plan on releasing the bash version which has somewhat spiralled out of control. It's not pretty, but functionally I think it does all most will need. I am hoping if I open source it smarterer people than I can refine it.

In the longer term, it should all be re-written in Python (which I am learning myself).

Stay tuned.. I promise it is coming! Lach

Like
SOLVED Posted: by maik.sanftenberg

@mm2270 I'm interested how you are leveraging this option and script to allow the people to choose what update they want.
Is it possible for you to share it? Thanks in advance.

Like
SOLVED Posted: by mm2270

@maik.sanftenberg \- stay tuned. I'm hashing something together for this, since I'm guessing it may be of some interest to some here.
What I posted above was an example. Yes, its a script, but it wasn't going any further than that dialog. I plan on making it more functional, more as an experiment or contribution to the community than anything else. We very likely would not use it where I am since all our clients are local admins and can run Mac App Store/Software Update whenever they want.

Like
SOLVED Posted: by dvasquez

@chifhirtle I am testing your updates script. Very nice work. I am using it on Mavericks and JAMF 8.73. I am a little confused on the "NonSysCore" functions/variables. Can you explain a little on that? Thanks and sorry for coming in late here.

Like
SOLVED Posted: by clifhirtle

@dvasquez see @acdesigntech's explanation on the NonSysCore calls here:

https://jamfnation.jamfsoftware.com/discussion.html?id=5404#responseChild33797
and here:
https://jamfnation.jamfsoftware.com/discussion.html?id=5404#responseChild38161

In short, these variables are a way to fire off additional, non-Apple SW update calls to other Casper policies. This could be updates for things like Flash, Java, or whatever. The catch is that you will need to be running HTTP-based distribution point(s) to take advantage of a policy within a policy, since SMB (AFP as well?) policies will already have the DP mounted for your primary SW update policy, thereby generating a "could not mount DP" error when it tries to call the NonSysCore sub-policies. For this reason I actually do not use them, instead simply running my Java/Flash/Office updates through separate policies, scoped to tiered deployment timeframes (from testing to users to directors), and scheduled through a traditional change management process.

Ultimately, I think we are all barking up the same tree here: pushing core security updates on X timeframe, permitting deferment within reason for major reboot updates, and integrating 3rd party security updates through either sub-policies or additional dialoging.

What I like about the pick and choose approach presented by @mm2270 above is that any of these dialogs are really just a veneer that can be tied back into any number of different Casper policies/deployments/actions/etc. This permits a greater freedom in imagining new approaches to user engagement, filling in the grey area between traditional Self Service (opt-in) and push policies (opt-out).

Like
SOLVED Posted: by JPDyson

@clifhirtle:

The catch is that you will need to be running HTTP-based distribution point(s) to take advantage of a policy within a policy, since SMB (AFP as well?) policies will already have the DP mounted for your primary SW update policy, thereby generating a "could not mount DP" error when it tries to call the NonSysCore sub-policies.

The workaround is to keep your kick-off/notification script in the end user's system, somewhere like /Library/Management/Scripts, and have your root policy simply invoke that script. No DP mounted, and the script can call your patching policies (like I've said before, I do this with a jamf policy -trigger patchme, and we have a group of policies with that custom trigger).

Like
SOLVED Posted: by dvasquez

Ah, ok so I would place the name of the smart group or group in the variable in the script and it will look for it when the array is triggered. example from mm2270: SoftwareUplift-Flip4Mac 2.4.4.2
The script will look for that and if a client is in that group it will also update that or those clients. I am not using HTTP-based distribution though. I also run my other updates like you mentioned by leveraging smart groups looking for X application and making the update available if needed. If that sounds right thanks so much. I guess I over looked those other posts. I appreciate your details and information. Theses scripts have been so helpful in achieving better Apple software update deployment for me and my environment.

Like
SOLVED Posted: by acdesigntech

@dvasquez you don't want to put in the entire group name though \- only the most-generic-while-still-being-unique part of the group name: example "SoftwareUplift-FlipForMac" instead of "SoftwareUplift-FlipForMac 2.4.4." Make sure that is also your Flip 4 Mac 2.4.4.2 update policy trigger.

This way you don't have to maintain the script per version that you release. All the script knows is that "oh there's an update group called SoftwareUplift-Flip4Mac and this Mac is in it, let's attempt a policy call with SoftwareUplift-Flip4Mac as the trigger.' And you just need to make sure the right packages are added to Casper and the upgrade policies. If you call out the entire name in the script, then you need to edit the script every time there's a new version.

Like
SOLVED Posted: by acdesigntech

I also just noticed that I have a logic error in my above script where it attempts to prompt the logged in user if there are updates that require a restart. Basically it's supposed to look for whether there were updates requiring a restart, THEN check to see if the timer is 0 or not (it does the opposite right now). I've posted the fixed script above and below. I also made it a bit nicer to tell the user WHAT updates are requiring a restart. Hat tip to @clifhirtle][/url for that bit of code:

#!/bin/sh

fRunUpdates ()
{

    ## Once the user OKs the updates or they run automatically, reset the timer to 5 
    echo "5" > /Library/Application\ Support/JAMF/.SoftwareUpdateTimer.txt

    /Library/Application\ Support/JAMF/bin/jamfHelper.app/Contents/MacOS/jamfHelper -windowType hud -lockhud -heading 'ISD is updating software on your computer' -description 'We are now updating your Mac System software. These updates should not take longer than 30 to 45 minutes depending on how many updates your Mac needs. If you see this screen for more than 45 minutes please call our Service Desk at X4949. Please do not turn off this computer. This message will go away when updates are complete.' -icon /Library/Application\ Support/JAMF/EndUserSupport/AGRose.icns > /dev/null 2>&1 &

    ## We'll need the pid of jamfHelper to kill it once the updates are complete
    JHPID=`echo "$!"`

    /usr/sbin/jamf policy -trigger SoftwareUpdate & 
    ## Get the Process ID of the last command run in the background ($!) and wait for it to complete (wait)
    SUPID=`echo "$!"`
    wait $SUPID

    ## kill the jamfHelper. If a restart is needed, the user will be prompted. If not the hud will just go away 
    kill -s KILL $JHPID
    exit 0
}



######### Set variables for the script ############

########## Get the group membership for the client #####################
## Get MAC Address using networksetup
MAC=$( networksetup -getmacaddress en0 | awk '{ print $3 }' | sed 's/:/./g' )

## Use the JSS API to get the Mac's group memberships
JSSGroups=$( curl -s -u username:password https://<casper server>:8443/JSSResource/computers/macaddress/$MAC \
| xpath //computer/groups_accounts/computer_group_memberships[1] \
| sed -e 's/<computer_group_memberships>//g;s/<\/computer_group_memberships>//g;s/<group>//g;s/<\/group>/\n/g' )

## Get the timer value
Timer=`cat /Library/Application\ Support/JAMF/.SoftwareUpdateTimer.txt`

## Get the currently logged in user, if any. Also check for updates that require a restart and ones that do not.
UpdatesNoRestart=`softwareupdate -l | grep recommended | grep -v restart`
RestartRequired=`softwareupdate -l | grep restart | grep -v '\*' | cut -d , -f 1`
LoggedInUser=`who | grep console | awk '{print $1}'`

################ End Variable Set ################

## Use echo and grep to find known-core (non system) software update groups. If these groups are found, run these installers silently since no restarts are required for these updates. Use an array to see which updates we take account of. The names of the array elements are also trigger names for each update. This way when there's a new software package to keep updated, we add the trigger name into the array, and the update policy to the JSS. Casper does the rest
NonSysCore=( 'SoftwareUplift-FlashPlayer' 'SoftwareUplift-Flip4Mac' 'SoftwareUplift-FontNuke' 'SoftwareUplift-PrintWindow' 'SoftwareUplift-MicrosoftOffice' 'SoftwareUplift-MicrosoftOutlook' )

for (( i = 0; i < ${#NonSysCore[@]}; i++ ))
do
    CheckUpdate=`echo "$JSSGroups" | grep "${NonSysCore[$i]}"`
    if [ "$CheckUpdate" != "" ]; then
        jamf policy -trigger "${NonSysCore[$i]}"
    fi
done

## If there are no system updates, quit
if [ "$UpdatesNoRestart" == "" -a "$RestartRequired" == "" ]; then
    echo "No updates at this time"
    exit 0
fi

## If we get to this point and beyond, there are updates. Check to see if there is a timer file on the Mac. This file tells the script how many more times it is allowed to be canceled by the user before forcing updates to install
if [ ! -e /Library/Application\ Support/JAMF/.SoftwareUpdateTimer.txt ]; then
    echo "5" > /Library/Application\ Support/JAMF/.SoftwareUpdateTimer.txt
fi

## if there is no one logged in, just run the updates
if [ "$LoggedInUser" == "" ]; then
    /usr/sbin/jamf policy -trigger SoftwareUpdate
else
    ## someone is logged in. prompt if any updates require a restart ONLY IF the update timer has not reached zero
    if [ "$RestartRequired" != "" ]; then
            ## If someone is logged in and they have not canceled 5 times already, prompt them to install updates that require a restart and state how many more times they can press 'cancel' before updates run automatically.
        if [ $Timer -gt 0 ]; then
            HELPER=`/Library/Application\ Support/JAMF/bin/jamfHelper.app/Contents/MacOS/jamfHelper -windowType utility -icon /Library/Application\ Support/JAMF/EndUserSupport/AGRose.icns -heading "AG ISD Approved Software Updates are Available for Your Mac" -description "These updates will require you to restart your Mac. If you would like to install these now, click 'Install Updates.' If you would not like to install now, click 'Cancel Updates.' You may choose not to install updates $Timer more times before this computer will automatically install them. These updates require a restart of your Mac: $RestartRequired" -button1 "Install Updates" -button2 "Cancel Updates" -cancelButton "2" -defaultButton 2 -timeout 60`
            echo "jamf helper result was $HELPER";
            ## If they click Install Updates then run the updates
            if [ "$HELPER" == "0" ]; then
                fRunUpdates
            else
            ## If no, then reduce the timer by 1. The script will run again the next day 
                let CurrTimer=$Timer-1
                echo "user chose No"
                echo "$CurrTimer" > /Library/Application\ Support/JAMF/.SoftwareUpdateTimer.txt
                exit 1
            fi
        else
            ## If Timer is already 0, run the updates automatically, the user has been warned!
            fRunUpdates
        fi
    fi
fi

## Install updates that do not require a restart
if [ "$UpdatesNoRestart" != "" ]; then
    /usr/sbin/jamf policy -trigger SoftwareUpdate 
fi
Like
SOLVED Posted: by jhalvorson

@acdesigntech \- Thanks for sharing the script. Please verify what you have for a custom event (aka trigger) titled "SoftwareUpdate".

I have my custom event policy set to the following Options:
General: Enabled, Custom with the name that matches the update script, Frequency = Ongoing.
(Not available offline and no other options filled in.)
Software Updates: Configured to use Apple's software update server
Restart Options: Restart if package or update requires it for both No User and User logged in. Delay is set to 5 minutes.

For short awhile I had enabled Maintenance with the option to Update Inventory, but that I believe that was just wasting time or causing issues if a restart was needed.

What I really want to know is if your using JAMF's Software Updates option in the policy or if you are using a Execute Command within the Files and Processes options, such as ```
software update -ia
```
[Casper Suite 9.22]

Like
SOLVED Posted: by acdesigntech

yeah it was just a policy that ran the single command softwareupdate -ia. Do I really need that to be in a separate policy? No I guess not.

I haven't relied on Caspers built in software update server tools for quite a while, especially as they became so unreliable in v8.x

Like
SOLVED Posted: by clifhirtle

Thanks for the reminder @JPDyson. Always tried to all my stuff server-side, but given some of our clients don't check in but 1x/mo I have increasingly been leveraging offline policies, which are similar enough for me to make an exception.

The workaround is to keep your kick-off/notification script in the end user's system
Like
SOLVED Posted: by mm2270

Hi all,

Just wanted to post some progress on what I showed up above. Here are some updated screenshots of what I'm hacking together.

An updated selection screen:
external image link

The dialog forces a selection to be made to click "Install" or they need to Cancel:
external image link

With some selections made:
external image link

Starting installations (at this point its checking the SUS or Apple's servers for the updates selected)
This is actually two oocoaDialog windows up simultaneously:
external image link

Now its going through (one of) the installations. In this shot, I chose some items that require a reboot, so the message reflects that:
external image link

Installs done. Reminder that a reboot is required.
external image link
The dialog times out after 30 seconds and then moves on to...

…the reboot countdown. Code courtesy of this thread (thanks to @alexjdale for his excellent enhanced version):
https://jamfnation.jamfsoftware.com/discussion.html?id=8360
external image link

A few more. This time installing updates with no required reboot. Note the different wording in the window
external image link

A final message when installing non-reboot updates
external image link

A couple of notes on this:
1- The script can use some Casper Suite script parameters that will let you customize a few aspects without needing to modify the script directly. For example:
a) the "Company" name in the Title bars of the windows
b) the number of minutes you want the countdown to run until restart. I set the default to 5 minutes, but you'll be able to pass any integer you want, like 10, 15, 60, whatever.
c) Some of the icons used in the dialogs
… and possibly a few other things

All of the above will use default values if you don't pass a parameter to it, so using parameters won't be a requirement for the script to run, just optional.

2 \- The script has two "modes" The default mode you see in the screenshots above shows progress for each update as it installs, then resets the progress bar back to 0 as it starts on the next update. The other mode will show one progress bar for all installs that slowly moves from 0 \- 100. The only issue with the latter mode is the text will likely need to be generic, as in "Installing 5 updates…" or something, because I'm having trouble getting that mode to correctly see which update is being installed as it goes along. The other mode doesn't have this issue.
The mode will probably end up also being a Casper Suite script parameter, so you can change it on the fly without needing to change the script itself.

3- I've tested this so far on 10.8 and 10.9, with some limited testing on 10.7. We don't have much Lion Macs in our environ right now so I don't know how much I'll be able to test it against that OS.

Finally, although this is directed at Apple software updates, this model could easily be adapted to work with regular Casper Suite polices, such as in the scripts posted above by @acdesigntech and @clifhirtle. Also, it could be integrated into some existing script workflows that allow for X number of deferrals before eventually forcing the installations. I haven't actually done that part yet, but it will definitely be possible.

I've got a little more work to do on fixing a few items, but otherwise in some basic test runs it seems to work pretty well.

Like
SOLVED Posted: by RaulSantos

@mm2270 this looks great any way I can contact you directly i have a few question.

Like
SOLVED Posted: by mm2270

@RaulSantos, best way is via email, mm2270 [at] me [dot] com

Like
SOLVED Posted: by alexjdale

Great stuff! Very similar to what I've implemented, but I have fewer options. My script reads in a list of updates we want to install and the user doesn't get to choose, they just know they have X number of critical updates that need to be installed and if one needs a restart. The script runs a "softwareupdate -l" to see what is available and on our list, then downloads the required updates before prompting the user to install them.

I also added in a mandatory date option. For the first week the user can cancel to defer until the next day, but it's forced after that date (the dialog tells them when that will happen). It's as simple as a parameter in YYYYMMDD format and checking to see if the current date is greater than/equal.

Mandatory date, reboot timer delay, and SWU list location (I keep the lists in text files uploaded to the JSS so they are in the Scripts folder) are configured in the script parameters, so each patch cycle we just update them as needed and start over again.

I'm very curious to see how you are executing the softwareupdate command. I had problems with it when running it from bash with a list of software updates and had to come up with a really stupid workaround (writing the command string to a file and then executing that file).

Like
SOLVED Posted: by dvasquez

@acdesigntech thank you for clarifying that. I think I understand that now, good stuff. With regards to @clifhirtle script I was unable to get the window to display available updates even though they are available. is that part dependent on this alone: (RestartRequired=softwareupdate -l | grep restart | grep -v '*' | cut -d , -f 1). I am still working on testing. I was able to get @lisacherie script to work like a charm. Thank you for that script! I think giving the user a list of updates that were detected to install that do require a reboot would be very nice. And speaking to what @JPDyson mentioned have any of you leveraged cron to execute tasks locally while still calling policy triggers? This would work nicely I believe.

Like
SOLVED Posted: by acdesigntech

@dvasquez][/url @alexjdale][/url yes, the ```
RestartRequired=softwareupdate -l | grep restart | grep -v '*' | cut -d , -f 1
``` line runs a list of available updates and only looks at the ones requiring a restart. the grep -v \* line gets the user-friendly name, like 'Mac OS X Update (10.8.5)' and not the ugly MacOSXUpdate10.8.5-blahblah nastiness. I spit that out in my jamfhelper line. Unfortunately bash is not as elegant as some languages with its text parameters, so if there are multiple restart-requiring updates, it just shows them as a single string. But it's good enough for my needs.

And no, I do not use a local daemon or anything to kick this off. My reasoning here is that the computer has to be on the internal network anyway to leverage our SUS, right? So it wouldn't help anything if the client was off the reservation and had a local daemon to launch a script that just contacts the internal JSS. Plus if there were ever changes to that initial script, it might be hard to get them out there. I guess that's where you get into a JSS in the DMZ. We're not quite there yet. Maybe next fiscal. Instead I have a second set of policies that use the same update script that are available via self service (and also offline) that are scoped to those Macs needing updates. Both of the policies first warn the user that they need to be on the internal network or VPN before they run them. So currently that's my workaround to the 'client that checks in once a month' issue \- which by the way is becoming more and more of a legit issue now that we are a 'globally minded workforce' (I take that as code for I WANT A LAPTOP! HMPH!!!).

@mm2270][/url yet again another great looking masterpiece! If my management wasn't already incensed about the pathetically low level of user interaction I'm already giving people, I would poach those ideas in a heartbeat! Kudos again to an amazing use of CD. I really do have to start getting into progress bars...

This really is great work, everyone! It's really cool to see all us Mac Admins chiming in and building solutions to problems. I'm glad I could add my little contribution!

Like
SOLVED Posted: by dvasquez

@acdesigntech Thanks for the reply. Right, when a machine is off the domain it would not have connection to the SUS. I put some checks to make sure that it is ping able. If not contactable it waits to check again later. But definitely a consideration. I did get the script you and @clifhirtle and @mm2270 added to this discussion to partially work it is quite amazing stuff. I am seeing some issues with scaling in the text and buttons. I also I added my own customizations to the title and others, maybe this is the cause. One issue I cannot get around is if I click to continue with the installations I always get the following error:

Jamf helper result was HELPER=2
user chose No

If I let it move through the postpones on the final one all works very well. I only get an error when I choose to install immediately. I added -startlaunchd to the helper portion of the scripts. This seemed to help in the past. I am still testing and thanks for all the contributions.

Dominic Vasquez

Like
SOLVED Posted: by dvasquez

I seem to have fixed the issues I was having. I believe editing this down "HELPER=HELPER=`/Library/Application..." helped. I edited that and made sure everything else was tidy and all is good. I took a "HELPER=" out. Not sure if that was intentional. Thanks again.

Like
SOLVED Posted: by acdesigntech

yeah that is certainly a typo

Like
SOLVED Posted: by tuinte

I just… I'm so impressed with the abilities on display in this thread.

Like
SOLVED Posted: by mm2270

Hi all,

I debated on whether to start up a new thread on this, but decided to keep this in here, despite the length this thread is getting to.

So, I’ve posted a version of the script that I showed above in the screenshots to a github page.
That page is here:
https://github.com/mm2270/CasperSuiteScripts/blob/master/selectable_SoftwareUpdate.sh

I’m admittedly a noob regarding Github, so hopefully how I’ve set it up will be easy for everyone to pull down the script.

Here are a few recent additions to this script I made since I last posted:

1- If there are Software Updates that do not require e reboot available to the Mac, an additional 3rd button shows up in the initial dialog, called “Install No Reboot Updates” Its a long-ish name for a button, but I wanted to make it descriptive enough that it was self explanatory without really needing to read a description in the dialog itself. That button does exactly what it describes \- goes right into installing all updates available that don’t require a reboot. My goal with this was to make it easy for an end user to install at least some of the presented updates. Giving them a one button install function that wouldn’t majorly disrupt their workflow seemed like a logical addition.
If the only updates available require a reboot this 3rd button doesn't show up (since that wouldn't make much sense :)

2- When installing updates, the progress bar now shows the number of the update its currently working on, in context of all selected updates. In other words, if a user selected 4 updates to install, the progress bar will indicate “Now installing 1 of 4 \- <Update Name>…” and iterate up by one for each that it moves to. This gives some better context to the user on how far along we are in the installations.

3- So this last one is a bit of a hack, but I put something together to help get around a known issue/bug with cocoaDialog. Namely, CD responds to the Command \+ Q keystroke and will close CD windows if they are active at the time. I personally think this is a bug, because, I believe any application that doesn’t have its own menubar or show up in the Cmd \+ Tab switcher should never respond to Cmd \+ Q.
Anyway, this behavior presents an issue because if the user does Cmd \+ Q, the progress bar and/or other windows will disappear, but the script continues to run, i.e. installs continue unabated, and that could be pretty confusing.
I came up with a workaround for this. Not the most elegant solution, but essentially, if the user quits one of the progress bars, as its going through the loop process, it checks to see if CD is in the process list and will re-run a sub-function to show the progress bar again. This is essentially a function within a function that can be called by a set of conditions. Cool thing is, it picks up exactly where it needs to and displays the progress bar already filled to where it needs to be, so it doesn’t really miss a beat in the install or reboot countdown. In my tests, this actually pops back up pretty quickly. so there's little lag between the quit and the progress bar reappearing.
While this only works right now on the progress bar functions, those are the most important ones. I believe the script responds to a Cmd \+ Q on any normal dialogs as if the user clicked Cancel, assuming that’s available. Otherwise it should act as if the default button was clicked. (Haven't thoroughly tested this last bit)

Couple more notes:
While this was developed around Software Update installs, this script could be adapted to work with Casper Suite policy runs, for those of you who are using custom solutions that use carefully named Smart Groups, like outlined above.

Lastly, the script is pretty self contained. You can pass a number of Casper Suite script parameters to it to change some items, but none of them are necessary. It will choose default values and strings if you don’t pass anything to it. It actually doesn’t even really rely on the Casper Suite as its designed. If Self Service isn’t available for one of the icons, it defaults to a System level icon. Its all commented pretty well in the script if you want to modify any of it, or understand which parameters you can pass to it.
The only real requirements are the 3.0 beta 7 version of cocoaDialog to be on the Mac, and a Mac with at least one available Software Update to run it against. The script exits silently if no updates are available,

Here’s an updated screen shot of the selection dialog
external image link

An updated shot of the progress bar, showing the new “Installing num of total” behavior:
external image link

There's a bit of stuff that could be added to this, like some proper error checking and trapping. Right now it will (edit) NOT correctly report if an installation failed. That could be integrated, but I didn't want to wait until everything was perfect or in place before letting folks take a look at it. So… work in progress.

If you have questions about anything, feel free to ask. Feedback welcome. Its all written in bash since that's all I can really script in right now. If you make improvements to it, feel free to let me know. Hopefully some of you will find it useful.

Like
SOLVED Posted: by mojo21221

@mm270 Sweet flow! Thanks I will definitely give this some testing.

Like
SOLVED Posted: by johnnasset

Good stuff in both scripts from @mm270 and @acdesigntech][/url

@mm270

Love the cocoadialog boxes and just the look and feel. Definitely gives more granular control to the end users. I could see this one being used for teacher machines. It would be nice to include the Postpone option as seen in @acdesigntech][/url version.

@acdesigntech][/url

I had to fiddle with this script a bit more. One thing I noticed when displaying the jamfhelper with the list of updates, some of the text gets cut off.

https://www.dropbox.com/s/22h24vr6spzsbnq/Screen%20Shot%202014-03-18%20at%202.16.03%20PM.png

Great for the minimal user interaction-I'd use this for student machines. I also like the ability to put non-core updates in the script (with some mods to the smart groups of course). Thanks to both of you for the awesome work!!

Like
SOLVED Posted: by acdesigntech

yeah JH has a character/line limit. not sure what it is, though it looks like 6 lines is the Max. Might switch to using cocoaDialog

Like
SOLVED Posted: by johnnasset

@acdesigntech

Okay so some odd behavior with your script. When logged in as a local admin, I'm not given the option to install or cancel, just the JH popup about installing software. This is the following I get in the log:

Script result: Found 1 nodes: -- NODE -- cat: /Library/Application Support/JAMF/.SoftwareUpdateTimer.txt: No such file or directory 2014-03-19 12:43:02.314 softwareupdate[1089:4403] PackageKit: Missing bundle path, skipping: 2014-03-19 12:43:17.283 softwareupdate[1103:2a07] PackageKit: Missing bundle path, skipping: /Library/Application Support/JAMF/tmp/notouch_SWU.sh: line 76: [: -gt: unary operator expected Checking for policies triggered by "SoftwareUpdate"... No policies were found for the "SoftwareUpdate" trigger. /Library/Application Support/JAMF/tmp/notouch_SWU.sh: line 20: kill: (1128) \- No such process

Any ideas (other than I forgot to change the SoftwareUpdate trigger to softwareupdate -ai)?

Like
SOLVED Posted: by acdesigntech

yes

/Library/Application Support/JAMF/tmp/notouch_SWU.sh: line 76: [: -gt: unary operator expected

That error means that the '$Timer' variable that holds the value in the .SoftwareUpdateTime.txt file (5, 4, 3, 2, 1, or 0 in my script) did not evaluate to a integer -- it either was nothing or a string, in which case the comparison would fail ( -gt is only for integer comparisons). It most likely did not evaulate to anything \- NULL or a blank, or something. If that comparison fails, the entire condition won't be met and it will default to the next item in the if..then..else statement which is to run the fRunUpdates function that shows the JH hud window about running updates.

Make sure you are starting your script off by creating the timer file if it's not already there:

if [ ! -e /Library/Application\ Support/JAMF/.SoftwareUpdateTimer.txt ]; then
    echo "5" > /Library/Application\ Support/JAMF/.SoftwareUpdateTimer.txt
fi

and then writing the new value (in my case it is current value \- 1) to the same file if the user chooses not to run updates. Make sure to use a > to overwrite the value already stored there, not a >> to append:

let CurrTimer=$Timer-1
echo "user chose No"
echo "$CurrTimer" > /Library/Application\ Support/JAMF/.SoftwareUpdateTimer.txt

Same goes for when the script actually DOES run updates. You need to reset that timer file for the next round of updates, whenever that is:

echo "5" > /Library/Application\ Support/JAMF/.SoftwareUpdateTimer.txt
Like
SOLVED Posted: by acdesigntech

derp... you'll want to make sure the line where your .SoftwareUpdateTimer.txt file is checked for a value is created before you try to get a value stored in there. On a first run of the script above it will fail to get a value -- thats the error you are seeing. On subsequent runs there will be a file created and you won't see that error any longer... I just love logic errors. Script above has been fixed.

Like
SOLVED Posted: by dvasquez

let CurrTimer=$Timer-1
echo "user chose No"
echo "$CurrTimer" > /Library/Application\ Support/JAMF/.SoftwareUpdateTimer.txt

Seems to already have been set in the script I have. Is there an issue with that?

I also noticed what @johnnasset mentioned regarding the go ahead to install updates. Also is there a way to make more room in the helper windows for "reboot" updates? Or is this truly a limitation. I also cut down on text in buttons. This helped greatly with look and feel.

I also added the creation of the SoftwareUpdateTime.txt file. I should have mentioned that. After my last edit things seemed to be better. I just figured I would have the script create the file other than having another policy to push it out. I was not aware that this needed to happen for other reasons.

echo "5" > /Library/Application\ Support/JAMF/.SoftwareUpdateTimer.txt

Was also already there and correct in my script. I think you had that right before. does that need to be places elsewhere?

Thank you for the help.

Like
SOLVED Posted: by RaulSantos

Look at the way Robotcloud is using the same type of idea. I would love to see there script. http://robotcloud.screenstepslive.com/s/2459/m/5322/l/87479-install-all-updates-policy

Like
SOLVED Posted: by johnnasset

And look at their innovative Self Service portal!! This seems familiar:

http://robotcloud.screenstepslive.com/s/2459/m/5322/l/76738-self-service

Like
SOLVED Posted: by mm2270

Its no secret RobotCloud is using the Casper Suite. They've been using it for years, but they've done some nice stuff around pulling data via the API and other methods into a completely custom UI. Looks nothing like the JSS, but the data is all from the JSS.

@dvasquez \- as far as I know, there is no way to get around the line limitation in jamfHelper. I ran into it several times myself when doing this kind of thing a year or so back. Its partly why I chose to do my windowing in cocoaDialog. jamfHelper still has some useful modes, like fullscreen and hud, but for general user facing window stuff, I stick with cocoaDialog.

Like
SOLVED Posted: by dvasquez

@acdesigntech][/url Hello. I am still seeing in test where when new updates are available the Helper prompt that lists updates that require an update does not come up. Instead it displays right away the "Installing Updates prompt". I made the adjustments you mentioned above. The logs do not mention anything specific or mentions successful completion. Also if I manually run the script and cancel at first sign of no list prompt then manually run it again it works correctly, I see the prompt with the list of updates. I also check the .SoftwareUpdateTimer.txt and it has been reset. Any ideas why this might still be happening. It seems to me that the script when run triggered by policy is not properly reading or locating the .SoftwareUpdateTimer.txt files when it first runs. I could be wrong though, of course.

Here is what I have:

## Once the user OKs the updates or they run automatically, reset the timer to 5 echo "5" > /Library/Application\ Support/JAMF/.SoftwareUpdateTimer.txt

and

if [ ! -e /Library/Application\ Support/JAMF/.SoftwareUpdateTimer.txt ]; then echo "5" > /Library/Application\ Support/JAMF/.SoftwareUpdateTimer.txt
fi

and

let CurrTimer=$Timer-1 echo "user chose No" echo "$CurrTimer" > /Library/Application\ Support/JAMF/.SoftwareUpdateTimer.txt exit 1 fi

I also had cut off text but shortened the text lines.
Thank you for your help.

Like
SOLVED Posted: by acdesigntech

dominic, if you post your script I might be able to help you. If you copy and paste my script above into your text editor and save it up to the JSS, it should work via policy. Make sure you check your line breaks and formatting though when copy/pasting. Copying from a website sometimes screws with my formatting.

Like
SOLVED Posted: by dvasquez

@acdesigntech I should check that. It is essentially your script edited to fit. I did not make any changes to major parts of your script. Thank you.

Like
SOLVED Posted: by agirardi

I hope that I am just doing something stupid, and this is a quick fix here. I just started testing this out, and I really like where this could go. My first time trying this on a machine running 10.9.1, I get the following error:

Software Updates are available, and a user is logged in. Moving to initial dialog... There are some non reboot updates available. Showing selection screen to user User chose to Cancel. Exiting...

Tried this a few times, rebooted, tried different user, but I don't see a selection box pop up, and I am certainly not hitting cancel.

Yup, it was pretty stupid. Answer was here:

The only real requirements are the 3.0 beta 7 version of cocoaDialog to be on the Mac, and a Mac with at least one available Software Update to run it against. The script exits silently if no updates are available,
Like
SOLVED Posted: by loceee

Ladies and gentlemenssss.

junki is here..

https://jamfnation.jamfsoftware.com/discussion.html?id=10636

Like
SOLVED Posted: by RobErnemt

Is there's a way to tweak this script to look for a policy with manual trigger instead of looking for an updates and let user postpone installation 3 times?

Like
SOLVED Posted: by ksanborn

I am fairly new to Casper to take it easy on me. We would like to automate the deployment of OS updates. All of the scripts in this thread great. We are running on to the following problem for the scripts we have tried so far. The message is No policies were found for the "runsoftwareupdate" trigger.

What does this mean? Do we need to create a second policy with a custom trigger value of "runsoftwareupdate"?

Like
SOLVED Posted: by mm2270

@ksanborn

Do we need to create a second policy with a custom trigger value of "runsoftwareupdate"?

Yes, anytime you do something like sudo jamf policy -trigger <sometriggername> The sometriggername must be set up as a policy with a custom trigger (referred to as "events" in Casper Suite 9). Generally this is enabled in place of any other triggers, like Recurring check-in, login, etc, but its possible to check more than one trigger.

Like
SOLVED Posted: by bpavlov

Yes, a trigger would be the custom trigger for another policy. As to what that other policy would contain, I'm not sure. As you can tell from this thread, there are a lot of different ways people are approaching this so I'm not sure what that particular script you are using had in mind specifically.

Like
SOLVED Posted: by ksanborn

@bpavlov Thank you. This helped. I don't have many computers in our testing environment that need updates still but I just ran the two policies on a computer and the update didn't install even though an update was found. Is this normal?

Like
SOLVED Posted: by mm2270

@ksanborn There are probably like 15 different scripts on this thread. I'm not sure if anyone can help you troubleshoot unless you let us know which script you are using and perhaps even post the script itself, even if its basically identical to what is already here, and some details on how you have this setup in your JSS. Otherwise, its going to be pretty hard to help out.

Like
SOLVED Posted: by lisacherie

The last version I posted in this crazy long thread should still work, though would need the syntax changed to be Casper 9 compatible on the line that calls the second policy.

If you are still stuck I can give you a little bit of help, though I don't know whose script and which iteration you are using :)

Like
SOLVED Posted: by ksanborn

All, thank you for your assistance. Everything is working now. My only question is, whether or not we can add a company logo to the message box? We already have the logo on the Macs. You guys are great. I did the following:

1.) I copied and pasted the script below in to a script in to JSS.

!/usr/bin/perl -w

use strict;

my $AVAILABLEUPDATES="";
my $CHANCESTOUPDATE=3;
my $COUNTFILE='/etc/SUScount.txt';
my $UPDATECOUNT=0;

$AVAILABLEUPDATES=/usr/sbin/softwareupdate --list;
chomp $AVAILABLEUPDATES;

printf "available updates is %s \n\n", "$AVAILABLEUPDATES";

unless (-e $COUNTFILE){ system "/bin/echo 0 > $COUNTFILE";
}

$UPDATECOUNT=/bin/cat $COUNTFILE;
printf "update count is $UPDATECOUNT\n\n";

If available updates contains * there are updates available

if ($AVAILABLEUPDATES=~/*/){

printf "there are updates available\n";

if ($AVAILABLEUPDATES=~/(restart)|(shut\sdown)/){

printf "updates need a restart\n";

my $LOGGEDINUSER='';

$LOGGEDINUSER=/usr/bin/who | /usr/bin/grep console | /usr/bin/cut -d " " -f 1; chomp $LOGGEDINUSER;

printf "value of logged in user is $LOGGEDINUSER..\n";

if ($LOGGEDINUSER=~/[a-zA-Z]/) {

printf "as there is a logged in user checking count\n";

my $CHANCESLEFT=$CHANCESTOUPDATE - $UPDATECOUNT; printf "Chances left is $CHANCESLEFT\n";

if ($CHANCESLEFT <= 0) { printf "No chances left installing updates and will reboot..\n"; system '/usr/sbin/jamf displayMessage -message "Your computer is installing updates and will reboot shortly."'; system "/usr/sbin/jamf policy -trigger runsoftwareupdate"; system "/bin/echo 0 > $COUNTFILE"; exit 0; }

else { printf "$CHANCESLEFT chances left... checking whether ok to restart\n"; my $ATTEMPT = $UPDATECOUNT 1; my $FINAL = $CHANCESTOUPDATE 1;

my $COMMAND= "\'/Library/Application Support/JAMF/bin/jamfHelper.app/Contents/MacOS/jamfHelper\' -startlaunchd -windowType utility -icon \'/Library/Application Support/JAMF/bin/jamfHelper.app/Contents/Resources/Message.png\' -heading \"Software Updates are Available for Your Computer\" -description \"This is installation attempt $ATTEMPT of $CHANCESTOUPDATE. On attempt $FINAL, updates will install automatically and your machine will restart.\" -button1 \"Yes\" -button2 \"Defer\" -cancelButton \"2\"";

my $RESPONSE = ""; $RESPONSE=system $COMMAND;

if ($RESPONSE eq "0") { printf "\nUser said YES to Updates\n"; system "/usr/sbin/jamf policy -trigger runsoftwareupdate"; system "/bin/echo 0 > $COUNTFILE"; exit 0; } else { printf "\nUser said NO to Updates update count is $UPDATECOUNT\n"; $UPDATECOUNT=$UPDATECOUNT + 1; system "/bin/echo $UPDATECOUNT > $COUNTFILE"; printf "\nUpdate count is now $UPDATECOUNT\n"; exit 0; } } } else { printf "no logged in user so ok to run updates\n"; system "/usr/sbin/jamf policy -trigger runsoftwareupdate"; system "/bin/echo 0 > $COUNTFILE"; exit 0; } } else { printf "no restart required\n"; system "/usr/sbin/jamf policy -trigger runsoftwareupdate"; system "/bin/echo 0 > $COUNTFILE"; exit 0; }
}
else { printf "there are no updates available\n"; system "/bin/echo 0 > $COUNTFILE"; exit 0;
}

exit 0;

2.) I created two policies. One policy runs at trigger Reoccurring Check-in, Execution Frequency Once per Month, and with a scope of smart group that contains all Macs that need updates.

3.) The second policy is called RunSoftwareUpdate and has the below set with a trigger of custom runsoftwareupate

Like
SOLVED Posted: by lisacherie

You can choose the icon to use with the -icon option when calling jamfHelper, or you can replace the icon included with jamf helper.

jamfHelper options can be viewed with this command:

/Library/Application\ Support/JAMF/bin/jamfHelper.app/Contents/MacOS/jamfHelper -help
Like
SOLVED Posted: by aaronbwcollins

Not to bring alive an old thread, but since it was so helpful, I thought I would add something I added to make it work in an environment that has an internal SUS.

function check_corp {

  CORP_URL="internalcompanyurl.com"
  check_corp="False"
  ping=`host -W .5 $CORP_URL`

  # If the ping fails - check_corp="False"
  [[ ! -z $ping ]] && check_corp="True"

  # Check if we are using a test
  [[ -n "$1" ]] && check_corp="$1"
}

check_corp

function reset_sus_url {
    ## Reset the SUS URL to whatever was being used prior to the script running.
    if [ "$SUS_URL_SET" == "1" ]; then
        /usr/sbin/softwareupdate --set-catalog $SUS_URL
    else
        /usr/sbin/softwareupdate --clear-catalog
    fi
}

internal_sus_url="http://yoursus.company.com:8088/index.sucatalog"

## Check to see what SUS URL is currently being used and set a variable to call later.

SUS_URL=$(defaults read /Library/Preferences/com.apple.SoftwareUpdate CatalogURL | grep $internal_sus_url)

if [ -z "$SUS_URL" ]; then
    echo "Using Apple Server"
    SUS_URL_SET=""
else
    echo "Using Mac Server"
    SUS_URL_SET="1"
fi

## If connected ensure that VPN is not active

if [ $check_corp == "True" ]; then
    vpn_status="$(ifconfig -lu | grep tun0 )"
    if [ ! -z "$vpn_status" ]; then
        echo "Currently Connected to VPN"
        reset_sus_url
        exit 100
    else
        /usr/sbin/softwareupdate --set-catalog $internal_sus_url
    fi
else
    /usr/sbin/softwareupdate --clear-catalog
fi
Like
SOLVED Posted: by MrP

Late to the party, but here is what I've written to handle this. It downloads the updates before it does anything to interact with the user. It then notifies the user updates are about to be installed and waits for acknowledgement before proceeding, so the user doesn't reboot in the middle of it. Once installed, if no reboot is required it notifies the user that no reboot is required and exits, so that they may reboot as desired without causing problems. If a reboot is required, it counts down 15 minutes before doing so,giving three prompts along the way, every 5 minutes. Each one says "reboot required, will happen automatically in N minutes" and has two buttons. "Reboot now" and "OK". If the user is logged in and the screensaver is active, indicating they are AFK, the script holds in a loop until the user is available to acknowledge. This prevents users screaming because they didn't save a document they had been working on all morning. If no user is logged in, the updates install and reboot automatically if needed.

The only thing I cannot get to work is the authenticated restart for encrypted volumes. I've tried the fde* command directly and tried to kick off a policy which has that configured. Neither ever boots back into the OS, which leaves the system at the filevault login with no way to remotely access it for further updates/inventory. Suggestions on that front welcome.

# Written by Paul Dickson

## Note: JAMF policy #350 is a policy that reboots with filevault authentication


touch /tmp/su.log
chmod 777 /tmp/su.log


b=softwareupdate -al | grep "found the following" | cut -d " " -f 3-5

# If updates were found, notify the user and begin install.
if [ "$b" == "found the following" ];
then

    # Marking updates as found for later script processing actions
    found="yes"

    # Caching/downloading updates before installation attempt
    softwareupdate -ad

    # Figure out if any of the updates require a restart
    softwareupdate -al | grep "[restart]"
    restart=$?


osascript << eof
-- Look to see if finder is running.  If it is then a user is logged in, so we will notify them about what is going on.
if application "Finder" is running and application "ScreenSaverEngine" is not running then 
    tell application "Finder"
        activate
        with timeout of 301 seconds
         display dialog "System updates are now being installed.  Please DO NOT shutdown or reboot until notified that the update process is complete." buttons {"Understood"} giving up after 300
        end timeout
    end tell
end if
eof

    # Installing all updates
    softwareupdate -ai

fi



# If updates were found this will run the jamf policy to update inventory on the server so that the server info on the computer reflects the new updates.
if [ "$found" == "yes" ]; then /usr/local/bin/jamf recon; fi


if [ "$restart" == "0" ]; 
then
# A restart is required to finish updates.  Notify user.

osascript << eof
-- Look to see if finder is running.  If it is then a user is logged in, so we will notify them about what is going on.
set a to 1
repeat until a = 0
    if application "Finder" is running and application "ScreenSaverEngine" is running then
        set a to 1
        --display dialog "1"
        do shell script "echo 'repeat 1 waiting' >> /tmp/su.log"
        delay 5
    else
        set a to 0
        --display dialog "0"
        do shell script "echo 'repeat 1 proceeding' >> /tmp/su.log"
    end if
end repeat
if application "Finder" is running then
do shell script "echo 'Finder running 1' >> /tmp/su.log"
    tell application "Finder"

    activate
    with timeout of 86400 seconds
        set question to display dialog "A reboot will be performed in 15 minutes to complete system updates.  Please save your work and exit all applications" buttons {"OK", "Reboot now!"} default button 1 giving up after 70000
        set answer to button returned of question

        if answer is equal to "Reboot Now!" then
            --tell application "Finder" to restart
            do shell script "jamf policy -id 350"
        else
            --delay 5
            delay 600
            if application "Finder" is running and application "ScreenSaverEngine" is not running then 
            do shell script "-n echo 'Finder running 2' >> /tmp/su.log"
                tell application "Finder"

                activate
                with timeout of 86399 seconds
                    -- This dialog will only wait for user confirmation for 60 seconds before continuing.
                    set question to display dialog "A reboot will be performed in 5 minutes to complete system updates.  Please save your work and exit all applications" buttons {"OK", "Reboot now!"} default button 1  giving up after 60
                    set answer to button returned of question

                    if answer is equal to "Reboot Now!" then
                            --tell application "Finder" to restart
                            do shell script "jamf policy -id 350"
                    else
                        --delay 5
                        delay 240
                        if application "Finder" is running and application "ScreenSaverEngine" is not running then 
                        do shell script "-n  echo 'Finder running 3' >> /tmp/su.log"
                            tell application "Finder"

                            activate
                            with timeout of 86398 seconds
                                -- This dialog will only wait for user confirmation for 60 seconds before continuing.
                                set question to display dialog "A reboot will be performed in 1 minute to complete system updates." buttons {"OK", "Reboot now!"} default button 1  giving up after 60
                                set answer to button returned of question
                            end timeout
                            end tell

                            if answer is equal to "Reboot Now!" then
                                --tell application "Finder" to restart
                                do shell script "jamf policy -id 350"
                            else
                                --delay 5
                                delay 60
                                if application "Finder" is running and application "ScreenSaverEngine" is not running then 
                                    do shell script "-n echo 'Tell Finder Restart 1' >> /tmp/su.log"
                                    --tell application "Finder" to restart
                                    do shell script "jamf policy -id 350"
                                end if
                            end if
                        end if
                    end if
                end timeout
                end tell
            end if
        end if
    end timeout
    end tell
end if
eof

echo 'jamf policy -id 350' >> /tmp/su.log
jamf policy -id 350

# Checking to see if updates were found.  As dictated by being an "elif", This only happens if a reboot isn't required but updates were found.  This lets them know a reboot is not required.
else
    if [ "$b" == "found the following" ];
    then
osascript << eof
if application "Finder" is running then
    tell application "Finder"
        activate
        with timeout of 3601 seconds
          display dialog "The software update process is now complete.  No reboot is required" buttons {"OK"} default button 1 giving up after 3600
        end timeout
    end tell
end if
eof
    fi
fi

exit 0
Like
SOLVED Posted: by hkabik

@MrP Have you tried

do shell script "jamf policy -id 350 &"

to kick off your authenticated restart? I've had luck in the past shifting policy trigger to the background when they are problematic in scripts.

Like
SOLVED Posted: by dmw3

@mm2270 running your Selectable_SoftwareUpdate.sh script on 10.11.5 comes back with syntax errors:

selectable_SoftwareUpdate.sh: line 461: syntax error near unexpected token `<'
selectable_SoftwareUpdate.sh: line 461: `done < <(echo "${readSWUs}")'

Any updates on this script?

Like
SOLVED Posted: by mm2270

Hi @dmw3 Can i ask how you're running the script? From the error you posted, I think you may be calling the script in Terminal by doing something like sh /path/to/Selectable_SoftwareUpdate.sh If so, the problem with doing that is by specifying sh up front, you're overriding the shell interpreter for the script, which is /bin/bash, not /bin/sh. This script uses some bash specific items in it, namely process substitution, which is what the error you posted is showing.

If you were running it that way, try by making the script executable first and then running it by just putting in the path to the script. It should work that way I think. That being said, I'm not sure if I've tried running this script on 10.11.5, so I'll work on that later today to see if I see any similar errors or other issues with it.

Like
SOLVED Posted: by mbezzo

crazy questions @mm2270 How much work would it be to change your script to not include check boxes? Make more of a "here's what's available to install" in a list. I've taken a quick look at the code, but curious on your thoughts.

Thanks!
Matt

Like
SOLVED Posted: by mm2270

@mbezzo Not crazy. Instead of removing the checkboxes, how about having them all checked but greyed out/disabled instead? Something like this?

Another option I thought of just now would be to have all the non-reboot updates checked and disabled, so the user can't refuse installs of those, but have the Reboot Required ones optional. It can be either not checked but check-able, like so:

Or, checked by default, but enabled for the user to uncheck it if they needed or wanted to.

All these options are possible, because cocoaDialog supports checked and disabled arrays that you can pass to it to tell it which checkbox items in the index should be set those ways. (Its why I continue to love the product and wish it would get some love to keep it viable)

Note that although I just whipped this up in about 5 minutes, I haven't actually tested an install run just yet. I will do that and see if it works as designed. I assume it should, but you know what they say about assuming. :)

Like
SOLVED Posted: by mbezzo

Looks good! Will definitely be playing with this next week. :)

Thanks as always @mm2270!

Enjoy the weekend,
Matt

Like
SOLVED Posted: by Xopher

@mm2270 thanks for the great script! Can you give a more advice on what items to change to have all updates checked but greyed out/disabled? We pretty much force all the updates to machines because they have gone through a couple rounds of test deployments before being sent out to all. Management also want the users to be able to defer updates that cause a reboot for a bit (15, 30, 1 hour or install now). Right now I have the reboot time parameter set to 30 min (if needed). So any advice on adding deferment like in your reboot_scheduler.sh (another great script)? Unfortunately my script-fu is not near good enough to meld to two scripts together successfully.

Like
SOLVED Posted: by mm2270

Hi @Xopher I hadn't thought of merging functionality from the 2 scripts together, but I suppose its something that could be done. Of course, you could just run one script after another in some way to make it all happen, but I know that's a bit more work and could end up with weird results.
I'll look into adding some deferment functionality into the Selectable Software Update script.

As for making the checkboxes greyed out and checked, I'll post that back here a little later. The dialog text needs to be adjusted as well since it doesn't make any sense to say something like 'select the updates to install' if they are all already selected for you.
I have to say though that after looking over the screenshots the other day, I'm not too crazy on this after all, only because the grey text (for the checked but enabled items) is so light its hard to read them clearly, so its not the best user experience in my opinion. Unfortunately, when items are disabled in a dialog, there's no way to control the coloring. That comes from the OS, not something built into cocoaDialog, not that I"d be able to change it there either.

Given this, I may see about adding an extra flag in the script that will simply list the updates that are going to be installed in plain (black) text rather than the checkboxes. It shouldn't be too hard to incorporate.

Like