Wednesday, May 18, 2016

Xcode Applescript, Part II

The JSS Side - Reading that data in:
In the last article (http://tmhoule.blogspot.com/2015/10/empower-your-users-with-simple-xcode.html) we created a simple application which writes a flag to the computer specifying if we could run updates on the computer or not.  Your checkbox could be for other things.  And once we get this app working, you can add other information that may be relevant in your environment - perhaps what software a computer is licensed for? or the name of the person responsible for the computer?

Now that the data is saved on the computer, we need to see it in our JSS so we can work with it.  To do that, we create an Extension Attribute.  The JSS has lots of built in items it looks for when running Inventory, such as Operating System version and RAM.  To look for things that are not there, JAMF allows us to write scripts to collect that information.

Click Settings -> Computer Mangagement -> Extension Attributes.  From there, click the + to create a new one.  Extension Attributes are run every time the computer does an inventory (recon).

To continue our last project, lets create an EA that shows the status of the AllowUpdates setting we were playing with last time. 

Give the EA a name - perhaps AllowUpdates so it is consistent and a description that makes sense to you.  We are collecting a boolean, but lets set the Data Type to 'String'.  The Inventory Display controls what tab the EA shows up in when looking at a computer record.  The Input Type should be Script.  This is the fun part.

Enter the following script.

#!/bin/sh
setting=`defaults read /Library/Preferences/com.toddCo.manage.plist AllowUpdates`

if [ $setting == 1 ]; then
    answer="True"
elif [ $setting == 0 ]; then
    answer="False"
else
    answer="No data"
fi


echo "<result>$answer</result>"



Then click Save.  The setting line runs the defaults command to get the AllowUpdates value it is currently set to and saves it in a variable called 'setting'.   The different if lines turn that 1 or 0 into something more understandable when we look at it in our JSS. 

Now if you go to a computer and Update Inventory (type jamf recon in Terminal).  When you look at that computer in the JSS, you should see AllowUpdates and either True or False.  If you get No Data, then you haven't run the program on that computer yet. 

Smart Groups:

Now it's time to build a group of computer in the JSS that allow us to update their computers.  In your JSS, click Smart Computer Groups on the left and click the + to create a new group.  Give it a name that fits such as 'Software Updates OK' and click Criteria.  Under Criteria, click + sign.  At the bottom of the list click Choose next to All Criteria and look for your EA.  In the example, we named it AllowUUpdates.  Click Choose next to it.  Leave the Operator set to 'is' and in the Value enter "True".  


Then click Save.  Now you have a group of computers that have run your program and checked that box allowing you to update them.  

So create a policy set to run Software Updates and set the scope to your new group.  Go ahead and update them - you've got their permission!


Hopefully, now you see the value of saving data with a local application, then reading it into your JSS.  You can flag computers that are kiosks so they be rebooted at convenient times- or servers so they can be rebooted only when the right people have been notified.  You may want to put in the name of a person responsible for a shared computer, should it need some attention. 

Friday, May 13, 2016

Adobe CC local server and RUM

If you have any significant installation of Adobe Creative Cloud products in your environment, it may makes sense for you to run a local Adobe update server which is basically a copy of all Adobe CC updates hosted on your own machine and server with the web server of your choice.

There are two major benefits to running this server.  First, you're speeding up the update process on your workstations and saving bandwidth to the Internet.  Second, having a local server allows you to control what updates are available to your machines. I run three servers so that I can have DEV, TESTING, and PRODUCTION.

You can also invoke Adobe's command line tool called Remote Update Manager (RUM) to look for updates and apply them. 

This guide will go over setting up your Adobe server and running RUM on your workstations.  I'll reference Apple Mac computers, but RUM works on Mac and Windows computers.  Sadly, the tool to sync your local Adobe Update repository with Adobe's doesn't work under Linux; it's Mac or Windows only. 

Setting Up a Server
All you really need is a Mac or Windows computer with a web server on it. You may need to modify the mime types when running under IIS, but that's not really too hard.  Then run the sync tool when you're ready.

Setting up the Webserver
You Mac has a web server built in.  It called Apache and is the server used by the majority of websites in the world.  To start it up, open Terminal and type.
     sudo apachectl start
You can verify it is working by opening a web browser and visiting http://localhost/   You should see "It Works!" which is a basic web page that comes with Apache.  Those web pages are kept in /Library/WebServer/Documents folder.  Open that path and create a folder in there called AdobeUpdates (no spaces).

Setting up AUSST
Now you need Adobe's AUSST tool (Adobe Update Server Setup Tool).  On your Mac, assuming you've installed Adobe Creative Cloud software (such as Photoshop or Illustrator), you can find it in /Library/Application Support/Adobe/OOBE/PDApp/CCP/utilities/AUSST    If you have Adobe Creative Suite installed on a different computer, you can copy the AdobeUpdateServerSetupTool to your webserver now. 

Sync Updates with Adobe
Once you have a web server running and have the AdobeUpdateServerSetupTool, you can run it to get the data from Adobe.  It'll download anywhere from 50 to 100 GB of data so be prepared!  Run the command line tool and pass it the parameters 'fresh' and the path to store the updates. 

<AUSST> <--fresh> </path/to/updates>

Example:
sudo /Library/Application\ Support/Adobe/OOBE/PDApp/CCP/utilities/AUSST/AdobeUpdateServerSetupTool --fresh /Library/WebServer/Documents/AdobeUpdates

That will start updates downloading. In the future to get changes from Adobe, you just need to run AUSST with the --incremental flag. --fresh is only for the first time you run it.

sudo /Library/Application\ Support/Adobe/OOBE/PDApp/CCP/utilities/AUSST/AdobeUpdateServerSetupTool --incremental /Library/WebServer/Documents/AdobeUpdates

Testing your server
Now is a good time to make sure everything works.  From a different computer, open a web browser and make sure you can connect to your webserver.  Enter the computer name or IP address into that web browser and you should see the 'It Works' page you saw in the beginning.

You should also be able to type in
     http://yourserver.com/AdobeUpdates/webfeed/oobe/aam20/mac/updaterfeed.xml
You should either see the file which looks like XML text, or get a parsing message about XML.  As long as you don't get file not found or can't connect to server. 

Configuring Clients
The Adobe CC programs on your company's workstation need to know about your server and be directed to get updates from there instead of directly from Adobe. To do that, they need a configuration file.  You can either create that manually and deploy them (one Mac and one Windows), or have the AUSST tool create it for you.

To have AUSST create it for you, enter a command like the following
<AUSST> --genclientconf=<path to where to save configs> --root=<path to updates> --url=<Server URL>

If you've been following along at home, your command should look like

sudo /Library/Application\ Support/Adobe/OOBE/PDApp/CCP/utilities/AUSST/AdobeUpdateServerSetupTool 
    --genclientconf=/Library/WebServer/Documents/AdobeUpdates/configs   
    --root=/Library/WebServer/Documents/AdobeUpdates 
    --url=http://servername/AdobeUpdates

This should be on one line, but formatting limits it here.

So now in the path specified in the genclientconf line, you'll have a mac folder and win folder.  Each has a file called AdobeUpdater.Overrides.  That file, when in the correct location on a client machine, instructs Adobe applications to check your server for updates instead of going out to Adobe's servers.  Copy that file to:
   In Mac OS X:
   /Library/Application Support/Adobe/AAMUpdater/1.0/AdobeUpdater.Overrides
   In Windows XP:
   \Documents and Settings\All Users\Application Data\Adobe\AAMUpdater\1.0\AdobeUpdater.Overrides
   In Windows 7/Vista:
   \ProgramData\Adobe\AAMUpdater\1.0\AdobeUpdater.Overrides
Your favorite management tool comes in handy here.

Testing it all out

If you've gotten this far, it's time to give it a try.  Open your favorite Adobe application, then from the Help menu, choose Check for Updates.  It doesn't matter if any are available or not - you can still see if it worked by going back to your server.  Open /Applications/Utilities/Console.   On the left, look under /var/log for apache2.  If you expand that disclosure triangle, you'll see access_log.  Select that and you should see an entry from the machine you just checked from.  Do it again, and watch the log file.  If an update is available, you'll see lots of lines as updates are downloaded.

RemoteUpdateManager (RUM)
If you remotely manage your machines with a tool such as JAMF Casper, you can create a policy to update applications using the RemoteUpdateManager (RUM).  Create a policy in your JSS with a 'Files and Processes' section Execute Command set to "/usr/local/bin/RemoteUpdateManager --action=install"    If any Adobe applications are currently open, the install will fail and the user will not be interrupted. 



Ongoing Work
Now that you have your own server, you'll need to update it occasionally.  Use the --incremental line from above to re-sync with Adobe and make updates available to your clients.

Next Time
In the next article of this series,  we'll incorporate a TEST server so that updates can be tested before being released to your computers. 

Wednesday, May 11, 2016

Extending App Update Tool

Extension Attribute
The first Article was about the App Update Tool.  It's a suite of scripts to allow you to update applications on your Mac.  This article will describe the Extension Attribute included with those scripts.

I wanted to know if the App Update Tool was running properly on computers.  Perhaps the LaunchD was removed? Perhaps a user edited or deleted the script so it would stop interruption of other work? 

In the Download Package is the Extension Attribute (EA) called 'UpdateToolEA.sh'.  If you are unfamiliar with JAMF Extension Attributes, they are scripts which run on machines when the 'Run Inventory' once a day and return whatever result they are configured to return.

This EA returns "Running OK" if all looks good, else it'll return if something has been edited or not running.  I have a Smart Group of 'Not 'running ok'' and just reinstall the client package on those machines. 

If you edit any of the client side scripts, you'll need to generate the MD5 of that file and add that to the EA.  To do that,
Go to a client machine which has the client scripts as you want them (you done making any edits?).  Then type
myMac$ md5 -q /usr/local/updateTool/updateInstaller.sh
b28d74377f41a6994f521b422e21d56f
That MD5 value will change if the script is edited.  So copy/paste that into the Extension Attribute.  The three scripts are
/usr/local/updateTool/updateInstaller.sh
/usr/local/updateTool/updateKicker-overdue.sh
/usr/local/updateTool/updateKicker-popup.sh 

There are three LaunchDaemons to start each of those scripts. 
/Library/LaunchDaemons/edu.todd.ll.updateToolInstaller.plist
/Library/LaunchDaemons/edu.todd.ll.updateToolKickerGUI.plist
/Library/LaunchDaemons/edu.todd.ll.updateToolKickerOverDue.plist
type md5 -q for each of those to get the correct MD5 to enter into your EA.

You can find the Extension Attribute in the Download Package App Update Tool.  Edit the EA so the correct MD5 values are in it.

Wednesday, May 4, 2016

Finding your $PATH

Finding your $PATH
..And other Environment Variables


When scripting, many users will develop a script on their own computer and get it all sweet just right.  However, when deploying the script with a management tool, such as JAMF Casper, the script may fail.  One of the primary reasons is the path to a command may not be available in the environment used to run your script.

What does that mean?  It means that those different commands you type in Terminal are applications that are saved in different folders on your computer.  Sometimes, the computer knows where to look for them, sometimes not. 

When you open Terminal, your session starts with some settings in place.  Those settings are saved in Environment variables seen by typing printenv in Terminal. 

MyMac$ printenv
TERM_PROGRAM=Apple_Terminal
TERM=xterm-256color
SHELL=/bin/bash
TMPDIR=/var/folders/0t/l5h4xta2m30z8brekdsm3_cp0l/T/
TERM_PROGRAM_VERSION=361.1
EDITOR=emacs
USER=tman
PATH=/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin
PWD=/Users/tman/Documents/CODE/NewProject
LANG=en_US.UTF-8
XPC_FLAGS=0x0
XPC_SERVICE_NAME=0
HOME=/Users/tman
SHLVL=1


The PATH variable is the one we are interested in here.  We can see the defined path by running echo and the variable name. 

MyMac$ echo $PATH
/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin


Here we have five different directories which can contain terminal commands.  Have you ever used diskutil or grep?  You can find out which directory a program is in by typing 'which' and the command name.

MyMac$ which grep
/usr/bin/grep
MyMac$ which diskutil
/usr/sbin/diskutil


which is actually a program itself.

MyMac$ which which
/usr/bin/which



So when you type a program to run, 'sudo' for example, the computer searches those directories in your PATH variable to find it.  Once it finds a program name that matches, it tries to run it for you.

Back to the original problem.  What happens when you run your nice script from an MDM?  Well, the PATH environment variable may not be set so the computer can't find the program you are referencing.  Say in your script, you are running

diskutil mount /tmp/Wonderapp.dmg
installer -pkg /Volumes/Wonderapp/Wonderapp.pkg -target / 

And that fails in your script when run through Casper or other MDM.  One possible reason is that when it runs from Casper, the Environment variables are not set so the computer can't find 'disktutil' or 'installer'. 

While you could define the path in the beginning of your script:

export PATH=$PATH:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin

The more common solution is to simply specify the full path to the program you are using. 

/usr/sbin/diskutil mount /tmp/Wonderapp.dmg
/usr/sbin/installer -pkg /Volumes/Wonderapp/Wonderapp.pkg -target /

You can see from the export line above how we add to the PATH variable.   The $PATH variable in the command means 'everything that's in PATH now' and add this other stuff to it.  If you have a directory with a command line tool you use which is not in your PATH, you can add it so you don't always need to type the full path to the program.  For example, I have a directory on my computer with programs I've written.  It's in /Users/tman/Documents/CODE/   So instead of specifying that path when I want to run one, I can simply add that directory to my PATH variable.

export PATH=$PATH:/Users/tman/Documents/CODE

Now when I run my app, the computer (or BASH actually) will 'look' into my CODE directory for that program.

SECURITY HINT: Many GreyBeards (old linux guys with long grey beards) will type the full path to 'sudo' and other programs that require passwords when running.  It's possible that someone puts a malicious copy of 'sudo' on your computer so when you try to run it and enter your password, it actually emails a copy of your password to them.  So the GreyBeards will always type /usr/bin/sudo to make sure the right copy is run and nobody has a second copy to run in it's place. 

Changes to your PATH variable, or other environment variables are only valid in that terminal session and are reset each time you open Terminal.  If you'd like that setting to stay, you need to add it to your bash_profile file.  That file, if it doesn't already exist, can be saved in your home directory. 

nano ~/.bash_profile

Notice the period before bash.  Any file that starts with a period is hidden from normal view. Add that same text as above.  

export PATH=$PATH:/Users/tman/Documents/CODE

Nano is a simple text editor.  You could edit with TextWrangler or another app, but since we're in the command line anyways....    Follow the on screen instructions to save and edit.  That command will automatically add my CODE directory to my path each time I start Terminal.


Friday, April 15, 2016

Customizing Adobe Acrobat Installs with the Feature Lockdown plist

If you've deployed Adobe Acrobat (Pro or Reader) in your environment, you may have used the Adobe Customization Wizard DC application. 

One of the checkboxes in there allow you to Enable Feature Lockdown by selecting a properly formatted plist.  However, creating that plist, properly formatted and with the settings required, is not as trivial as it may seem.  Additionally, Adobe has some settings which can be confusing.

I spent more time that I'd like to admit using basic text editors (emacs, textwrangler) to create a plist.  Sadly, they all resulted in 'corrupt' plist files.  Besides Adobe Customization Wizard DC not reading them in, Xcode also wouldn't see my files as a valid plist, so I can't blame Adobe for that one. 

I find it easiest to create a blank plist by using the defaults command.
defaults write /Users/yourname/Desktop/AdobeLockdown.plist adobe -bool NO
Once you have a plist, you'll need to edit it.  On modern computers, Xcode is the best bet.  Property list editor is another option if you have it available.  As you may be working with multiple level dictionaries, Plist Buddy is an option, but is not recommended. 

Open your plist.  If you have Xcode installed, just double-click it.

Select the line that says adobe on it and delete it.  That was  our placeholder to generate a plist to work with.  When you select the line that says 'Root', you'll see a small + sign that you can click.  That creates a new line.

Adobe's documentation shows '11' in their examples.  That '11' refers to the version number of Adobe Acrobat you are working with.  As of this writing, there are two current version, '2015', and 'DC'.  DC is their 'Continuous track' while '2015' is a more traditional version scheme.  You'll need the software version to match what you put in the key here.  Read this link to decide which track is right for your organization.
http://www.adobe.com/devnet-docs/acrobatetk/tools/AdminGuide/whatsnewdc.html
Noteworthy, DC does not allow you to disable Adobe Cloud sign in or some other features.  DC stands for Distributed Cloud and thus online functionality tends to be required.

My organization is deploying 2015 so I created a new key in my plist called '2015'.  Set the type to Dictionary.  Under that, I created another line, as a child of 2015, called FeatureLockdown.

Again, the type is Dictionary.

Now is the time to start adding your Lockdown Features.  They are documented by Adobe at
http://www.adobe.com/devnet-docs/acrobatetk/tools/PrefRef/Windows/index.html
I found reading the Windows reference was easier as it showed the path needed for the key.  For example, if you want to disable rendering of Flash content in the PDF, you'll add a child key to Feature Lockdown called 'bEnableFlash' of type boolean set to NO.

By looking at the HKLM Path, you can see that 'bEnableFlash' comes after FeatureLockdown. 

Some features requre parent dictionaries.  For example, lets say you want to disable Webmail integration.  Looking at the documentation under WebMail, you can see 'bDisableWebmail' has a HKLM Path of "HKLM\SOFTWARE\Policies\Adobe\(product name)\(version)\FeatureLockdown\cWebmailProfiles".   To translate that to your plist, under FeatureLockdown, create a new key of type dictionary called 'cWebmailProfiles'.  Under that, create a new boolean key called 'bDisableWebmail' set to YES. 

Here you can see I also added 'bUpdater' under FeatureLockdown.  That prevents Acrobat from updating itself.  As I'm in a managed environment, I update Acrobat after it's been tested here.


Interestingly, there is a second control called 'bUpdater'.  While the one mentioned above (as a child of FeatureLockdown) references Acrobat's ability to run its built in update mechanism, the second is under Workflows in the documentation under Services integration.  This 'bUpdater' setting disables updates to web-plugin components and all services.  Below, you can see both bUpdater lines as well as a few other settings I decided to set.

 
Be careful of some settings, such as 'bToggleWebConnectors' which require a YES to lock it out, while other settings require NO to lock it out.  The documentation is critical.  

You may notice that many of the settings start with the letter 'b'.  That tells Acrobat to expect a boolean (yes or no) setting.  Others begin with 'c' which, in Apple terms, is a Dictionary, containing sub entries. You may also see 't' which tells Acrobat to expect a text string for the data type.  


Tuesday, November 10, 2015

Introduction to pkgbuild

Since Apple's PackageMaker went away, people have been looking for a good solid tool to build installer packages.  JAMF's Composer gets a lot of attention and works for many.  However it's not right in all situations, cost being one factor.

Apple supplies pkgbuild with developer tools and, while being a command line tool, is not as hard as one might think.  While pkgbuild is amazingly powerful, I'll only be covering an introduction to it here so you get get a basic app out the door.

Start by building a directory structure.

THE SETUP....
PACKAGEROOT --
   ---ROOTofINSTALLFILES
   ---SCRIPTS

PACKAGEROOT is your folder that keeps all the junk about this stuff in there. I might keep the command line string to build the installer in a .txt file for later reference. 

ROOTofINSTALLFILES is the base installation folder.  If you are installing an Application to the Applications folder, then you either put the Application in this folder and set the install-location to /Applications   or you create a folder called Applications with your program inside it and set the install-location to /.    I personally do that latter, so that if needed, I can later easily add a /Library folder with any LaunchDaemon that I might need to add.  Be sure permissions are correct before building your installer.

SCRIPTS folder contains any postinstall or preinstall scripts and is optional (if you don't have any scripts, ignore it).  These are BASH scripts named simply as postinstall or preinstall with no file extensions and they are executed either before or after the installation takes place.  It's useful if you need to backup data or check for a certain condition before installing anything.  If either scripts exits with a non-zero status, the installer will report as failed.


OTHER INFO YOU'LL NEED...
An Identifier:  you'll need a unique identifier for this package.  Generally a reverse domain name with the package name appended will ensure it is unique.  "com.companyname.packagename.pkg" is a good example.
A Version number: Increment this when you distribute a new version of the installer.  This way computers will know if they are upgrading an existing installer, or repeating something already done.



IF YOU BUILD IT....
Run pkgbuild to build your installer.  Open terminal and 'cd' (change directory) into your PACKAGEROOT.  The easiest way to do that is open terminal and type 'cd' followed by a space.  Then drag the PACKAGEROOT folder into the Terminal window and it'll auto-type the full path for you.

Then type the command:
pgkbuild 
 --root <drag ROOTofINSTALLFILES folder here> 
 --identifier "com.companyname.packagename.pkg" 
 --version "1.0" 
 --install-location "/" 
 --scripts "<drag SCRIPTS folder here>  
  NewInstallerPackageName.pkg

I wrote a little tool to encourage users to update their Adobe CC applications.  Here's an example of my installer build.
pkgbuild 
   --root /Users/todd/Desktop/AdobeCCSoftware/AdobeCCUpdater
   --identifier "edu.school.department.AdobeCCUpdater.pkg" 
   --version "1.0" 
   --install-location "/" 
   --scripts /Users/todd/Desktop/AdobeCCSoftware/Scripts AdobeCCUpdateTool.pkg

Note that these commands are all on one line, only separated here for better formatting. 



TROUBLESHOOTING....Go ahead and run your new installer.  If you have an error, especially with scripts involved, be sure to use the 'echo' command in your script.  While the installer is open, from the Window menu, choose Installer Log and set the PopUp to "Show All Logs".  You can then see exactly what your installer is doing.  I also recommend you test your installer on a test box, not your own machine. 

Also be sure to increment your version number when building a new installer. 



COMING UP....
Another great tool is Apple's pkgutil which allows you to manipulate existing packages.  It's great when you want to see what a postinstall script looks like or what else is included in an installer. 

Friday, October 16, 2015

Empower your Users with Simple Xcode apps

At one point, I worked in a large, distributed environment. Sadly, I didn't have authority over any computer in our management system.  The only way I could take action was if the end user specifically authorized me.  And with 10,000 machines all over the city, that was not trivial to do.  

Actually, it turned out to be surprisingly closer to trivial that I expected.  If you are familiar with the defaults command, I was able to save a setting on a user's computer, to read in with an extension attribute in JAMF Casper and create a smart group.  But how to get the end users to run that Defaults command was the challenge.  

The command I needed was  defaults write /Library/Preferences/com.toddCo.manage.plist AllowUpdates -bool YES.

AllowUpdates means that I'm allowed to push out software updates, such as Office, Java, Flash, and more.  And the end user needed to submit that command on their computer to approve me updating their machines.  

I ended up writing a simple application in Xcode which ran that command for them.  I'll walk you through that process in the remainder of this guide.

Open Xcode and create a new project.  Select Other and choose Cocoa AppleScript.
Give your project a name and enter your Organization Identifier (a reverse domain name is best) and click Next.  Then save the project somewhere appropriate and get ready to do the fun part.

When building an application, it's often best (and most fun) to start with the GUI (Graphic User Interface).  So lets layout our application.  Click on MainMenu.xib on the left, then click the icon for The Window (See circled below) to make sure the main program window appears. 
To start adding elements to our window, click the third icon on the bottom right.  There, you can see buttons, progress bars, check boxes, labels, text fields and more.  Find a Multi-Line Label and put in some text such as "Checking this checkbox will allow the IT Department to update applications such as Java, Flash, or Firefox on your computer".  Then, of course, drag in a check box and set the text to 'I Agree'.

Now to make the 'I Agree' check box do something in code.  Select the check box in your window, then select the Binding Inspector.  That's the 7th icon along the top right section of the screen that looks like a spiral.  See the image below.  If necessary, use the disclosure triangle to expand the 'Value' area.  Check the box to Bind To and choose 'Application'.  And enter a variable name in the Model Key Path field.  I choose to use the variable name 'iAgreeBox'. 


Before we leave this screen, also drag in a Push Button and call it 'Save'.  We'll make that work later. 

Now it's time to write some code to make stuff happen.  Towards the top left of the screen, click on AppDelegate.applescript to bring up the code window.   If we want to get or edit any data in the GUI, we need to reference it with a property.  Since we want to be able to see what that check box is set to, lets create a property for it.  At the top of AppDelegate.applescript, you'll see a 'Property theWindow' line.  Below that, add the text 'property iAgreeBox : false'    The name iAgreeBox needs to match exactly the name you put in that Model Key Path on the previous step.  I often copy/paste just to avoid spelling errors.  Spelling errors don't count if you make the same mistake in both places.  Now we can use iAgreeBox to determine the state of that checkbox in our script. 

Lets add some code for the Save button.  Below the property lines, enter this text:
     on saveTime_(sender)
        do shell script "defaults write /Library/Preferences/com.toddCo.manage.plist AllowUpdates -bool " & iAgreeBox with administrator privileges

         display dialog "Setting Saved!" buttons "OK"


    end saveTime_

Note that there is an underscore after 'saveTime' and the word 'sender' in parens.  That is all required.  Why it's required will be discussed elsewhere.

Lets see what that means in parts.  The first line, on saveTime_(sender) means that this chunk of code should run when the program issues a call to run the task named saveTime.   We'll make that 'Save' button from the GUI do that later.  And at the end, end saveTime_, is the footer, or end, for the steps of the saveTime task.

The  do shell script line is the big one.   Since this is an AppleScript program, we tell Applescript to run some terminal/command line code.  The defaults write command is the one from the beginning of the article.  That's where the Yes or No is saved into that plist file.  With Administrator Privileges means that if needed, the program will prompt for an admin name and password.  Only someone with admin writes will be able to run this program. 

Now we need to link the save button from the GUI to run this chunk of code.  To do that, switch back to MainMenu.xib on the left to see the GUI.  Hold down the Control key on the keyboard and drag from your button to the icon for Delegate.  See the screen image below.  

Once you drag to delegate, a small dark window will appear with a list of those different tasks in it.  One should be called 'saveTime'.   Select it to link the button to that task.  Now when someone clicks the button, the steps in the 'saveTime' block of code will run! 

You now have a fully functional program! How do you know if it works?  Since we're writing some data to a plist, we can just look there and see what it says.

defaults read /Library/Preferences/com.toddco.manage.plist AllowUpdates

Now try running your new program and change the checkbox and click Save.  Each time you open the application, it does default to an 'off' checkbox instead of what is saved there.  We'll fix that next.

Find the section that has applicationWillFinishLaunching.  This is the chunk of code that runs when the program is opened.  Add this line in between so it runs when you start your program.

    on applicationWillFinishLaunching_(aNotification)
         set my iAgreeBox to (do shell script "defaults read /Library/Preferences/com.toddCo.manage.plist AllowUpdates")
    end applicationWillFinishLaunching_


Now when you open the program, the checkbox should match what it was set to last time it was closed.  

 
Next:
In the next post, we will read that setting into our JAMF JSS as an Extension Attribute and create a Smart Group to target those machines. 
http://tmhoule.blogspot.com/2016/05/xcode-applescript-part-ii.html