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.
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.
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).
@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.
Clif, I see that (inconsistently) as well. This is the main reason I want to go to http distribution points.
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.
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.
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
@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.
@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.
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
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
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
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}')
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?
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.
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.
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.
Clif, would you mind reposting your final script for comparison to what others have posted?
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
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 :)
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.
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.
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.