jamfhelper software update trigger

jwojda
Valued Contributor II

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.

1 ACCEPTED SOLUTION

acdesigntech
Contributor II

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

View solution in original post

226 REPLIES 226

acdesigntech
Contributor II

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

jwojda
Valued Contributor II

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

jarednichols
Honored Contributor

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.

talkingmoose
Moderator
Moderator
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!

Cem
Valued Contributor

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

lisacherie
Contributor II

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

jwojda
Valued Contributor II

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?

acdesigntech
Contributor II

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.

lisacherie
Contributor II

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 

", "$AVAILABLEUPDATES";

# If available updates contains * there are updates available

if ($AVAILABLEUPDATES=~/*/){

    printf "there are updates available
";

    if ($AVAILABLEUPDATES=~/restart/){

        printf "updates need a restart
";

        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..
";

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

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

            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
";

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

lisacherie
Contributor II

@jwojda

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

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.

lisacherie
Contributor II

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

jwojda
Valued Contributor II

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.

UESCDurandal
Contributor II

@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"?

lisacherie
Contributor II

@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 :)

lisacherie
Contributor II

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 

", "$AVAILABLEUPDATES";

# If available updates contains * there are updates available

if ($AVAILABLEUPDATES=~/*/){

        printf "there are updates available
";

        if ($AVAILABLEUPDATES=~/restart/){

                printf "updates need a restart
";

                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..
";

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

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

                        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 "
User said YES to Updates
";
                                system "/usr/sbin/jamf policy -trigger runsoftwareupdate";
                                exit 0;
                        } else {
                                printf "
User said NO to Updates
";
                                exit 0;
                        }
                }
                else {
                        printf "no logged in user so ok to run updates
";
                        system "/usr/sbin/jamf policy -trigger runsoftwareupdate";
                        exit 0;
                }
        }
        else {
                printf "no restart required
";
                system "/usr/sbin/jamf policy -trigger runsoftwareupdate";
                exit 0;
        }
}
else {
        printf "there are no updates available
";
        exit 0;
}
exit 0;

UESCDurandal
Contributor II

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

lisacherie
Contributor II

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.

jwojda
Valued Contributor II

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.

lisacherie
Contributor II

@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.

jwojda
Valued Contributor II

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 

", "$AVAILABLEUPDATES";

# If available updates contains * there are updates available

if ($AVAILABLEUPDATES=~/*/){

        printf "there are updates available
";

        if ($AVAILABLEUPDATES=~/restart/){

                printf "updates need a restart
";

                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..
";

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

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

                        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 "
User said YES to Updates
";
                                system '/System/Library/CoreServices/Software Update.app/Contents/MacOS/Software Update';
                                exit 0;
                        } else {
                                printf "
User said NO to Updates
";
                                exit 0;
                        }
                }
                else {
                        printf "no logged in user so ok to run updates
";
                        system '/System/Library/CoreServices/Software Update.app/Contents/MacOS/Software Update';
                        exit 0;
                }
        }
        else {
                printf "no restart required
";
                system '/System/Library/CoreServices/Software Update.app/Contents/MacOS/Software Update';
                exit 0;
        }
}
else {
        printf "there are no updates available
";
        exit 0;
}
exit 0;

UESCDurandal
Contributor II

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...

lisacherie
Contributor II

@UESCDurandal

Good catch! Not many MPs where I am.

Try changing this line:

if ($AVAILABLEUPDATES=~/restart/){

To:

if ($AVAILABLEUPDATES=~/(restart)|(shutsdown)/){

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

UESCDurandal
Contributor II

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.

acdesigntech
Contributor II

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 "$!"
```

chappj01
New Contributor

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)

rderewianko
Valued Contributor II

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!

chappj01
New Contributor

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)

chappj01
New Contributor

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

acdesigntech
Contributor II

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

maiksanftenberg
Contributor II

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.

jhbush
Valued Contributor II

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

acdesigntech
Contributor II

@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

stevewood
Honored Contributor II
Honored Contributor II

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.

stevewood
Honored Contributor II
Honored Contributor II
 

maiksanftenberg
Contributor II

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

jhbush
Valued Contributor II

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

Sonic84
Contributor III

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

lisacherie
Contributor II

Try using this argument when calling the jamfHelper

-startlaunchd

lisacherie
Contributor II

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 

", "$AVAILABLEUPDATES";

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

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

";

# If available updates contains * there are updates available

if ($AVAILABLEUPDATES=~/*/){

    printf "there are updates available
";

    if ($AVAILABLEUPDATES=~/(restart)|(shutsdown)/){

        printf "updates need a restart
";

        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..
";

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

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

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

            if ($CHANCESLEFT <= 0) {
                printf "No chances left installing updates and will reboot..
";
                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
";
                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 "
User said YES to Updates
";
                    system "/usr/sbin/jamf policy -trigger runsoftwareupdate";
                    system "/bin/echo 0 > $COUNTFILE";
                    exit 0;
                } else {
                    printf "
User said NO to Updates update count is $UPDATECOUNT
";
                    $UPDATECOUNT=$UPDATECOUNT + 1;
                    system "/bin/echo $UPDATECOUNT > $COUNTFILE";
                    printf "
Update count is now $UPDATECOUNT
";
                    exit 0;
                }
            }
        } else {
            printf "no logged in user so ok to run updates
";
            system "/usr/sbin/jamf policy -trigger runsoftwareupdate";
            system "/bin/echo 0 > $COUNTFILE";
            exit 0;
        }
    } 
    else {
        printf "no restart required
";
        system "/usr/sbin/jamf policy -trigger runsoftwareupdate";
        system "/bin/echo 0 > $COUNTFILE";
        exit 0;
    }
}
else {
    printf "there are no updates available
";
    system "/bin/echo 0 > $COUNTFILE";
    exit 0;
}
exit 0;