jamfhelper software update trigger

ImAMacGuy
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

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

jhbush
Valued Contributor II

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.

acdesigntech
Contributor II

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.

rsnediker
New Contributor

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 

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

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

        printf "> Updates require 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 "> Checking count as there is a logged in user.
";

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

            if ($CHANCESLEFT <= 0) {
                printf "> No chances left, installing updates and will reboot...
";
                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.
";
                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.
";
                    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 
";
                    system "/usr/sbin/jamf policy -trigger runsoftwareupdate";
                    system "/bin/echo 0 > $COUNTFILE";
                    exit 0;
                } else {
                    printf " - User said NO to updates.
";
                    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
> Update count is now $UPDATECOUNT
";
                    printf "> Chances left is $CHANCESLEFT
";
                    exit 0;
                }
            }
        } else {
            printf "> No logged in user. Okay to run updates.
";
            system "/usr/sbin/jamf policy -trigger runsoftwareupdate";
            system "/bin/echo 0 > $COUNTFILE";
            exit 0;
        }
    } 
    else {
        printf "> Updates do not require a restart. Installing.
";
        system "/usr/sbin/jamf policy -trigger runsoftwareupdate";
        system "/bin/echo 0 > $COUNTFILE";
        exit 0;
    }
}
else {
    printf "> There are no updates available. Exiting.
";
    system "/bin/echo 0 > $COUNTFILE";
    exit 0;
}
exit 0;

jenski
New Contributor

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!

mm2270
Legendary Contributor III

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.

quedayone
Contributor

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

acdesigntech
Contributor II

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

ImAMacGuy
Valued Contributor II

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.

clifhirtle
Contributor II

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?

acdesigntech
Contributor II

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

clifhirtle
Contributor II

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.

acdesigntech
Contributor II

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.

JPDyson
Valued Contributor

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

clifhirtle
Contributor II

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

JPDyson
Valued Contributor

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

acdesigntech
Contributor II

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.

clifhirtle
Contributor II

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.

joemamasmac
New Contributor III

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 

", "$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-Z0-9]*/) {

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;

Thanks for any suggestions.

Joe

lisacherie
Contributor II

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

JPDyson
Valued Contributor

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

maiksanftenberg
Contributor II

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

clifhirtle
Contributor II

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

stevewood
Honored Contributor II
Honored Contributor II

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

mm2270
Legendary Contributor III

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}')

clifhirtle
Contributor II

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?

clifhirtle
Contributor II

double post

mm2270
Legendary Contributor III

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.

clifhirtle
Contributor II

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.

mm2270
Legendary Contributor III

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.

jhbush
Valued Contributor II

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

clifhirtle
Contributor II

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

acdesigntech
Contributor II

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

clifhirtle
Contributor II

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.

acdesigntech
Contributor II

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.

loceee
Contributor

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.

Kumarasinghe
Valued Contributor

@loceee
Thanks.

golbiga
Contributor III
Contributor III

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

ImAMacGuy
Valued Contributor II

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?

loceee
Contributor

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