Skip to main content

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.

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


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



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


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



#!/bin/sh

fRunUpdates ()
{

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

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

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

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

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



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

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

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

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

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

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

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

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

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

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

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

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

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



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



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



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


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



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


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



The workaround is to keep your kick-off/notification script in the end user's system

Hi all,



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



An updated selection screen:
external image link



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



With some selections made:
external image link



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



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



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



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



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



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



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



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



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



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



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



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


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


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


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



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



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



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


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


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



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



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



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


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



Jamf helper result was HELPER=2
user chose No



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



Dominic Vasquez


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


yeah that is certainly a typo


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


Hi all,



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



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



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



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



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



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



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



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



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



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



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



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



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


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


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



@mm270



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



@acdesigntech][/url



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



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



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


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


@acdesigntech



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



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



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


yes



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


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



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



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


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



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


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



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

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


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



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



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



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



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



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



Thank you for the help.


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