foigus' Notes

Mostly OS X Admin Related

Something in the AIR – AIR 20, 64 bit, and AIR Applications

Recently I was asked to deploy an updated Adobe AIR-based application.  I asked the requestor if they’d like the most current version of AIR (version 20.0.0.233) to go along with it and the response was “Sure”.  I installed the application and imported it into Munki with a straightforward munkiimport of the application itself.

Later in the day I received a call back from the requestor letting me know the newly updated application crashed instantly upon launch.  Digging further, I discovered that when the application was launched, the following happened:

  • Air Application.app/Contents/MacOS/Air Application
    was renamed to
    Air Application.app/Contents/MacOS/Air Application_32
  • A new Air Application.app/Contents/MacOS/Air Application was generated

Upon further examination, this appears to be a result of AIR 20 becoming a 64-bit runtime.  An Adobe support article (Issues while downgrading from AIR 20 to a lower version on Mac OS X) says the following:

After you install AIR 20, any previously installed AIR app using the Shared Runtime that is launched gets updated. So the app’s launcher code will now be a 64-bit binary. The previously used 32-bit launcher gets renamed with a ‘_32’ suffix.

This provides an interesting issue for system administrators:

  • The bit architecture of an AIR application is a reflection of the version of AIR (either pre-AIR 20 or AIR 20 and beyond) that was installed when the AIR application was installed if the application was installed from an “.air” archive. Note an “.air” archive requires AIR to manually be installed on the target computer before installation of the “.air” archive
  • The bit architecture of an AIR application is a reflection of the developer’s computer used to package the AIR application if the application was a “Native Installer”.  Note a Native Installer can include AIR
  • If the installed version of AIR is upgraded from a previous version of AIR to AIR 20, an existing AIR-based application will attempt to generate a 64-bit executable in the “MacOS” directory
  • The default POSIX permissions of the “MacOS” directory of an AIR-based application are 755 (i.e. only the owner can modify the directory) and the default POSIX ownership of an AIR-based application is either root:admin (if a Native Installer was used and no version of AIR was previously installed) or the default user and group of the user who installed the application.  These do not work well in a managed environment
  • If the application cannot rewrite its executable, it may prompt for administrative rights (and refuse to run without them) or just crash (I’ve seen both)

Previously-distributed AIR applications that may have functioned normally under AIR 19 may not initially run properly with AIR 20.  Options to handle AIR 20 might include:

  • Repackaging 64-bit versions of AIR-based applications.  These would need to be generated by installing AIR 20, running a 32-bit AIR-based application, and then capturing and repackaging the resulting application
  • Reinstalling AIR-based applications following the installation of AIR 20, however this will only work for .air-archive based installations
  • Giving users write permission to the “MacOS” directory for all AIR-based applications, however this is a last resort

Also notable is that applications repackaged on a computer with AIR 20 may not function properly on computers where an earlier version of AIR is installed.  This is due to the 64-bit executable being packaged under AIR 20 and being distributed to a computer with an earlier 32-bit version of AIR.

To determine what architecture an executable is, use the “file” command:

$ file /Applications/Zinio\ Reader\ 4.app/Contents/MacOS/Zinio\ Reader\ 4 
/Applications/Zinio Reader 4.app/Contents/MacOS/Zinio Reader 4: Mach-O 64-bit executable x86_64

Ensure AIR-based applications are 64-bit ready so they continue to work when AIR 20 is installed.

Packaging Adobe Rapid Release Updates With CCP or AAMEE

Recently I was presented with an Adobe Rapid Release update and requested to install it on a handful of computers.  Adobe’s Rapid Release program provides access to betas and hotfixes for Adobe products (as opposed to a rapid release schedule such as Firefox’s).  Rapid Release installers:

The Rapid Release update was provided on a dmg in a double-clickable “AdobePatchInstaller.app” format, which doesn’t play well with software distribution systems.  Since the Rapid Release update isn’t available through any of the normal channels, I reached out to Adobe_ITToolkit via Twitter and asked what I could do with the update.  The response was:

If it is an update to an existing major version it should work via the Offline media workflow.

The “Offline Media” workflow is generally intended for situations where bandwidth is limited, but in this case it can be used to package updates that would otherwise not be available.  The procedure to package the update:

For AAMEE

  • Disconnect the computer where AAMEE is installed from the Internet (see note following step 15 on page 34, which explains that preference will always be given to updates from the Internet)
  • Open AAMEE
  • Click “New Update Package”
  • Give the package a name and a location to be saved
  • The check for updates will fail (“The update server is not responding”)–click “Continue”
  • Click “Add Update”
  • Navigate to the update dmg and click “Open”
  • Proceed normally with packaging the update

For CCP

  • Open CCP
  • Navigate to the point where “Applications & Updates” are offered (there’s so many different workflows to reach this point I doubt I can cover them all)
  • Click “Add Offline Media”
  • Click the folder icon to browse for the update dmg
  • Navigate to the parent folder for the update dmg and click “Open” (No, the dmg is not directly selectable. No, I don’t know why)
  • Click “Extract”
  • Confirm the desired update appears in the lower portion of the “Add Offline Media” window and is checked
  • Click “Done”
  • Confirm the desired update appears in the “Applications & Updates” list and is checked (note this may require clicking “Show archived versions”)
  • Proceed normally with packaging the update

Then import the AAMEE/CCP pkg into your favorite software distribution system and install.

Thanks to the folks behind the Adobe_ITToolkit Twitter account for pointing me in the right direction.

ChoiceChangesXML and Office 2011

A post I wrote in JAMFNation that probably could have been it’s own blog post.  However fit in pretty well to the thread there–take a look.

SMB2 and 3 Enrichment Reading

Ever since Apple announced they were replacing Apple Filing Protocol (AFP) with Server Message Block (SMB) as the default file sharing language in OS X 10.9 “Mavericks” and beyond, Mac admins have had a love and hate relationship with trying to get SMB working in their environment.  A love of not having to be the odd man out in the file sharing world, and a hate of trying to make SMB perform as reliably as the well-worn AFP.  If your environment is supporting Macs that use SMB servers, a handful of links should be enlightening (and required) reading regarding why performance issues exist.  While these links alone won’t fix issues, they will provide insight into what’s going on:

If your current file sharing vendor doesn’t support a feature like “vfs_fruit” (the last bullet above), use the above links to write a feature request for your vendor.  In our case, we submitted a NetApp feature request (FPVR-00046972, feel free to add the voice of your organization!) to implement the “AAPL” SMB2 Create Context as discussed in the SMB2 source for OS X 10.9 Mavericks and OS X 10.10 Yosemite.

Disabling Java 8 Sponsor Offer Installation

Oracle Java 8u40 for OS X includes a new, unwanted payload–for those who aren’t paying attention, clicking right through the installation means the ask.com toolbar is now installed on their computer.  Oracle does offer a support page which details installing Java 8u40 without the toolbar, but the options boil down to two techniques:

  1. Install Java first so sponsor offers can be disabled though the Java Control Panel
  2. Use the command line and pass in the appropriate argument

#1 is kinda silly (installing software just to access a setting seems a bit unusual), and while #2 isn’t so bad it’s not the path the Java 8 updater wants to take you.  The Java 8 updater happily reattempts to install the sponsor offers.  So let’s say there are users with admin rights in your organization who theoretically could update or reinstall Java 8, how can we prevent sponsor offers on their computers?  Using fseventer, let’s see what files are modified when technique #1 above is applied:

defaults read ~/Library/Preferences/com.oracle.javadeployment.plist

{
 "/com/oracle/javadeployment/" = {
 "deployment.modified.timestamp" = 1426392515218;
 "deployment.version" = 8;
 "install.disable.sponsor.offers" = true;
 };
}

defaults read ~/Library/Application\ Support/JREInstaller/ThirdParty.plist

{
  SPONSORS = 0;
}

cat ~/Library/Application\ Support/Oracle/Java/Deployment/deployment.properties

#deployment.properties
#Sat Mar 14 21:11:41 PDT 2015
deployment.modified.timestamp=1426392701602
deployment.version=8
install.disable.sponsor.offers=true
#Java Deployment jre's
#Sat Mar 14 21:11:41 PDT 2015
deployment.javaws.jre.0.registered=true
deployment.javaws.jre.0.platform=1.8
deployment.javaws.jre.0.osname=Mac OS X
deployment.javaws.jre.0.path=/Library/Internet Plug-Ins/JavaAppletPlugin.plugin/Contents/Home/bin/java
deployment.javaws.jre.0.product=1.8.0_40
deployment.javaws.jre.0.osarch=x86_64
deployment.javaws.jre.0.location=http\://java.sun.com/products/autodl/j2se
deployment.javaws.jre.0.enabled=true
deployment.javaws.jre.0.args=

Beyond this, there are mentions across the Internet of placing the following lines in the system’s deployment.properties:

install.disable.sponsor.offers=true
install.disable.sponsor.offers.locked

However in my testing, currently the only file that matters is ThirdParty.plist, and the Oracle Java 8 installer only (per opensnoop) looks in the user’s home for this file:

  501    209 cfprefsd       4 /Users/admin/Library/Preferences/com.apple.HIToolbox.plist 
  501    851 MacJREInstaller  -1 /Users/admin/Library/Autosave Information/Oracle.MacJREInstaller.plist 
  501    851 MacJREInstaller  27 /Users/admin/Library/Application Support/JREInstaller/JREInstallLog.txt 
  501    851 MacJREInstaller  -1 /Users/admin/Library/Application Support/JREInstaller/ThirdParty.plist 
  501    851 MacJREInstaller  27 /Users/admin/Library/Application Support/JREInstaller/JREInstallLog.txt 
  501    851 MacJREInstaller  27 /.vol/16777218/185166 
  501    851 MacJREInstaller  27 /Users/admin/Library/Application Support/JREInstaller/JREInstallLog.txt

Setting any or all of the above files except ThirdParty.plist and the sponsor offers are still offered.  ThirdParty.plist can be set with the following command:

defaults write ~/Library/Application\ Support/JREInstaller/ThirdParty SPONSORS -string "0"

run via a LaunchAgent, Outset, or some equivalent method.  Once ThirdParty.plist is set, future GUI installations of Java 8 will completely skip the sponsor offers step and immediately install Java.  Of course, keep in mind this all could change with the next release of Java.

Thanks to Johannes Seitz for researching the situation.  I was working on this so late at night I don’t recall whether Johannes originally wrote the above command or if I did (or maybe we both reached the same conclusion).  Cheers Johannes!

Outlook 2011 Folder Item Count Recommendations

Recently a user asked what the limits were for good performance in Outlook 2011.  Checking around the various support articles I found (italics mine):

So the number of items is in question and also whether or not subfolders count toward that number.  I contacted Microsoft support and asked which answers were correct–here is the response:

  • “I would recommend keeping the Inbox, Sent and Deleted items folders below 10,000 items if possible.”
  • “Keep a maximum of 20,000 items each in the Inbox and Sent Items folders (includes the folders and subfolders).”
  • “The Calendar, Contacts and Tasks should stay below 5,000 if possible.”

Note that these recommendations were easier for Microsoft support since my company uses Office 365 Hosted Exchange and thus the Exchange environment was known by support.  Different Exchange servers may not be able to support this sort of load and may require lower item counts.

Managing Java 7 and 8 Updates

Controlling your Caffeination Level
As part of improving software deployment at my organization, I tackled managing the Java 7 and 8 updater.  There’s a lack of Mac-based examples for this process (many thanks to Tim Sutton for his posts here and here, JAMF Nation-ites for this post, and Grivet-Tools for this post), so this is my contribution. The configuration file format for Java 7 is documented here and for Java 8 is documented here.  There are a pair of files:

  • deployment.config: Located at /Library/Application Support/Oracle/Java/Deployment/deployment.config, this file tells Java where to find the deployment.properties file (discussed below) and whether or not the deployment.properties file is mandatory.  If the deployment.properties file is mandatory and cannot be found, no Java software will be allowed to run.
  • deployment.properties: A file listing various Java settings and optionally locking those settings.  Note that due to Safari sandboxing restrictions, deployment.properties must be located under /Library/Application Support in order for the Java plugin running inside Safari to access it (unless the Java plugin is running in Unsafe Mode).  A huge thanks to Michael Lynn (who goes by the IRC nickname “frogor” in the discussion about this restriction) for assistance with determining this requirement.

My deployment.config and deployment.properties files are below. First, deployment.config:

#deployment.config
deployment.system.config=file:///Library/Application%20Support/Oracle/Java/Deployment/deployment.properties
deployment.system.config.mandatory=true
  • #deployment.config is a comment, and thus ignored
  • deployment.system.config details where the deployment.properties file can be found
  • deployment.system.config.mandatory=true tells Java the deployment.properties file is required.  If deployment.properties isn’t available Java code will not be executed

And then deployment.properties:

#deployment.properties
deployment.macosx.check.update=false
deployment.macosx.check.update.locked
deployment.expiration.check.enabled=false
deployment.expiration.check.enabled.locked
deployment.security.level=MEDIUM
deployment.security.level.locked
  • #deployment.properties is a comment, and thus ignored
  • deployment.macosx.check.update=false causes Java to not check for updates when the Java browser plugin runs.  However note the LaunchAgent-based background updater still checks for updates (we’ll handle that later)
  • deployment.macosx.check.update.locked prevents changing the deployment.macosx.check.update setting
  • deployment.expiration.check.enabled=false tells Java to not check to see if the current version of Java is expired.  Note Java has two expiration dates–one expiration date is the date of the next planned release (currently quarterly) if the computer can reach the Internet, while the second expiration date is 30 days after the next planned release date.  For example expiration dates, see the “Java Expiration Date” sections of the Java 8 Release Highlights
  • deployment.expiration.check.enabled.locked prevents changes to the deployment.expiration.check.enabled setting
  • deployment.security.level=MEDIUM sets the default Java security level to medium, but note the security level “medium” only exists with Java versions earlier than Java 8u20 (where the default security level was changed to “high”).  More details about Java security levels are located in the “Security levels in the Java Control Panel” section of this Oracle support page
  • deployment.security.level.locked prevents changes to the deployment.security.level setting

But wait, there’s more!
In addition to the expiration and update checks that occur when running a Java applet in a browser, there also is an LaunchAgent-invoked background Java updater (the LaunchAgent job is located at /Library/LaunchAgents/com.oracle.java.Java-Updater.plist) that will periodically check for Java updates and prompt for an update if one is available.  Unfortunately this prompt is shown to users who cannot actually install the Java update.  This automatic update check can be disabled with the following:

sudo defaults write /Library/Preferences/com.oracle.java.Java-Updater JavaAutoUpdateEnabled -bool false

It’s very tempting to try to set this preference via a management system, however I did not have success with any of the following methods:

It appears the preference must literally be set in /Library/Preferences/com.oracle.java.Java-Updater.plist or it’s ignored.  Thankfully it’s easy to check to see whether this preference is set properly by setting the Java plugin to run in debug mode and then invoke the updater by hand:

export JPI_PLUGIN2_DEBUG=1

/Library/Internet\ Plug-Ins/JavaAppletPlugin.plugin/Contents/Resources/Java\ Updater.app/Contents/MacOS/Java\ Updater -bgcheck
2015-02-18 21:50:26.053 Java Updater[886:21122] Java Update Check is disabled

The Hits Just Keep on Coming
While testing this article, another settings file kept appearing at ~/Library/Preferences/com.oracle.javadeployment.plist. This file has very interesting looking keys:

$ defaults read /Users/admin/Library/Preferences/com.oracle.javadeployment
{
 "/com/oracle/javadeployment/" = {
 "deployment.expiration.check.enabled" = false;
 "deployment.macosx.check.update" = false;
 "deployment.modified.timestamp" = 1424322701333;
 "deployment.security.level" = MEDIUM;
 "deployment.version" = "7.21";
 };
}

A simple way to set these preferences? Alas, no. It appears that Java treats this file as a cache, generated from deployment.properties. Once I started testing whether just com.oracle.javadeployment.plist was enough to manage these settings, I discovered when I ran the Java plugin it reacted by wiping out most of the preferences in this file. There is one exception I could find. It appears that deployment.expiration.check.enabled is consulted from this file first, before deployment.properties.  I noticed this because of the following scenario:

  • Install proper deployment.properties and deployment.config files
  • Install Java
  • Set the computer’s clock forward six months (thus Java was months beyond both expiration dates of the plugin)
  • Run the Java plugin in Safari
  • Be prompted the Java plugin had expired
  • Quit Safari
  • Run the Java plugin in Safari again
  • See no prompt the Java plugin had expired

This scenario does react to the initial setting of deployment.expiration.check.enabled in com.oracle.javadeployment.plist, however it will be subsequently overwritten by the setting in deployment.properties.  This behavior is possibly part of the “native cache” Oracle mentions in this statement regarding deployment.expiration.check.enabled:

Note: To ensure that the expiration check is disabled, use the -userConfig deployment.expiration.check.enabled false option with the javaws command. If this property is changed in the deployment.properties file, open the Java Control Panel before starting an application to ensure that the native cache is synchronized with the file. Otherwise, the change might be ignored the first time an application is started.

Amusingly deployment.expiration.check.enabled can be handled properly via a Profile, however it should also be set in deployment.properties.

Wrap-up
After all of this, if the following is done:

  • Set settings via deployment.config and deployment.properties
  • Disable the background updater by writing a preference to /Library/Preferences/com.oracle.java.Java-Updater.plist
  • Disable plugin expiration in com.oracle.javadeployment (via a Profile or equivalent method)

Java should function even past expiration, however:

The easiest way to test all of the above is to download and install an out-of-date version of Java (so the security and expiration prompts will appear) and test these settings.  Note testing how Java 8 reacts to being expired is more difficult since the lowest security level (“high”) requires signed Java applets and successful validation of those signatures, thus it’s not possible to move the computer’s clock forward six months and have those signatures successfully validate.  In this case, make sure to test against a version of Java 8 that is old enough to be past the “secondary [expiration] mechanism” per the release notes.

More Thoughts on Munki’s AutoRemove

Previously I had discussed using autoremove for Adobe CC 2014 Installer Items, believing the best option to conserve disk space was to automatically remove Adobe CC since “Hey, if I didn’t put it there, it’s not needed.”  A couple of recent experiences has changed my mind.

  • I recently installed the Adobe CC 2014 suite on a computer where some Adobe CC 2014 applications were already installed.  While the client’s manifest was set up properly, I use the “site_default” manifest as a starting point to configure a handful of settings.  Unfortunately I chose to include the “production” catalog in the “site_default” manifest.  Since “site_default” (naturally) doesn’t include any Adobe CC 2014 products, Munki checked software for removal and attempted to remove the Adobe CC 2014 software it discovered.  In order to prevent future unintended actions, I created a separate “site-default-catalog” catalog for the “site_default” manifest, thus lowering the chance I shoot myself in the foot.*
  • Both the “old” CS6 suite and the “new” Adobe CC 2014 suite include the now-discontinued Fireworks CS6. I had both Fireworks CS6 packaged by AAMEE (licensed via ETLA) and Fireworks CS6 packaged by CCP (licensed via Creative Cloud) available in my Munki repo as unique Installer Items. I then installed Fireworks CS6 from the AAMEE-based Installer Item.  On the next managedsoftwareupdate run Munki noticed Fireworks CS6 was installed and the Fireworks CS6 Installer Item from the CCP-based installer was not included in the client’s manifest and therefore should be removed. Thus Munki had it’s own titanic battle with itself, alternately uninstalling and reinstalling Fireworks CS6 with each subsequent managedsoftwareupdate run.

*Note I didn’t say Munki succeeded in uninstalling the CC 2014 software.  Since I was still testing Munki 2.1’s support of CCP pkgs, I hadn’t yet made Munki 2.1 available in the “production” catalog.  Thus the CCP Installer Item’s uninstall_method of “AdobeCCPUninstaller” wasn’t understood by the older Munki client and uninstall failed.

Due to the two items above I’ve decided that setting “autoremove” to true for Adobe CC 2014 isn’t the best choice.  I’ve pared back my “autoremove” to the serialization Installer Items rather than the Adobe CC 2014 Installer Items themselves.

(Re)Activating T-Mobile Data for Life on a Replaced iPad

Over the last week I had a difficult time reactivating an iPad with T-Mobile’s Free Data for Life.  The iPad was originally set up with Free Data for Life around February, and it worked for the infrequent occasions I needed cellular data.  In order to not accidentally use the data, I turned off cellular data when I wasn’t actively and intentionally using cellular data. About five months ago the iPad was damaged and replaced under AppleCare+ and the T-Mobile SIM was transferred to the replacement iPad.

A week ago I enabled cellular data on the iPad and was unable to access the Internet, repeatedly being redirected to a page where I could purchase additional data.  It turns out that I hadn’t enabled cellular data since the iPad was replaced–not surprising the data plan wasn’t working since the replacement iPad was a different device than originally registered for the data.  However when I contacted T-Mobile support it didn’t seem they knew what to do when confronted with a replacement iPad.  T-Mobile support had me try at least the following, while repeatedly assuring me the account was ok:

  • Wait a few days for the IMEI to “update in the system”
  • Verify there was a T-Mobile signal
  • Verify there was no T-Mobile outage in the area
  • Reseat the SIM card
  • Verify the APN settings
  • Reset network settings
  • Enable Data Roaming
  • Reset the iPad back to factory settings (I refused to do this)
  • Attempt to re-register through t-mobile.com/connectme
  • Have T-Mobile support change the IMEI on file to match the replacement iPad’s IMEI
  • Reset Subscriber Services
  • Purchase a replacement SIM card and tried to self activate service (T-Mobile Twitter support credited the cost of the replacement SIM to my T-Mobile account)
  • Have T-Mobile support activate the replacement SIM and waited two hours
  • Multiple restarts of the iPad (after practically every step above)
  • Talk to (actually passed to) Apple Support (fortunately Apple was closed when this was tried)

The solution was to have T-Mobile support delete the existing Free Data for Life plan and service telephone number from their system and assign a new phone number (and no service plan) to the SIM and IMEI.  The iPad was then prompted to create or sign in with an existing T-Mobile ID (I used my existing ID).  Then I needed to pick a data plan:

  • “Always Connected”: Postpaid subscriptions of GB or MB of data a month
  • “Data on the Fly”: Data “packs” that expire after a period of time or when exhausted
  • “Plain and Simple”: No paid data

The “Plain and Simple” option is what I wanted, since that provides the monthly 200MB of free data without any contract or any billing relationship with T-Mobile at all.  Once I set up the new data “plan”, cellular data was once again working. If you’re considering using T-Mobile’s Free Data for Life plan, make sure you save certain information because it will be necessary if you have to talk to T-Mobile Support:

  • The T-Mobile ID (the email address used when the account was created)
  • The T-Mobile account password (although T-Mobile support never asked for this, it was necessary when signing into the T-Mobile account)
  • The phone number assigned to the iPad (available from the “View Account” button under the “Cellular Data” section of “Settings”
  • The account PIN (note this PIN is emailed to you upon successful registration, unfortunately I did not know that until after I had worked with T-Mobile Support to reset the PIN)
  • The SIM card number
  • The IMEI number of the iPad

Hopefully the issues I experienced getting T-Mobile service up and running on a replacement iPad were because I wasn’t a paying customer.  However I’d think the whole idea behind the Free Data for Life plan is to get T-Mobile’s SIM into as many iPads as possible, and difficulties like this make it hard to use T-Mobile’s service.

Distributing DPS Desktop Tools for InDesign CC 2014 with Munki

Adobe’s Digital Publishing Suite (DPS) is a group of add-on tools and services that facilitates tablet publications.  Let’s examine distributing the desktop software portion of these tools, “DPS Desktop Tools for InDesign CC 2014”, with Munki.

Red Herrings
It turns out DPS Desktop Tools for InDesign CC 2014 (hereafter referred to as DPS Desktop Tools) is surprisingly difficult to deploy.  Reviewing over the options:

  • Repackaging is generally considered harmful.  Attempting to collect the products of an installation and reproduce the intention of the original installer is difficult.  Although my organization is repackaging DPS Desktop Tools for InDesign CS6 (and I know others are as well) I wanted to avoid this if possible with DPS Desktop Tools for InDesign CC 2014.
  • Creative Cloud Packager (CCP) (and its predecessor, Adobe Application Manager Enterprise Edition (AAMEE)) intentionally block DPS Desktop Tools updates.  This is because DPS Desktop Tools’ “inclusion [in a CCP package] results in a much higher rate of failure of the deployment package“.  Sometimes DPS Desktop Tools accidentally sneaks into a CCP package, but the results are failure-prone.
  • Command-line (CLI) installation per the Adobe support article for installing DPS Desktop Tools mentions “Enter[ing] administrator credentials when prompted”, which means a non-administrator can’t install the software.  Also, per the support article CLI installation requires “…that client or user is logged in..”, complicating distribution.
  • CLI installation with administrative rights isn’t mentioned in the Adobe support article for installing DPS Desktop Tools.  It turns out this isn’t because it was forgotten–instead CLI installation with administrative rights (e.g. “sudo”) leads to no authentication prompt, but also leads to a near-guaranteed installation hang.  Earlier versions of the Adobe support article for DPS Desktop Tools mentioned “sudo”, implying there was more flexibility during installation, but this appears to either be in error or installation with “sudo” stopped working at some point.
  • CLI installation with the bsexec hack, used when a command needs to run in a different context than the current context, is a possible option.  Munki uses it to install some Adobe software, but my testing results appeared to show that installation via the bsexec hack fails regardless of whether a user is logged in or not.

Given all the above, deployment of DPS Desktop Tools appears to be a dead subject.  Repackaging must be the only way…

Accidental Installation
…Luckily, no one had written up the above information when I examined deploying DPS Desktop Tools via Munki.  I downloaded the installer, munkiimported it, and it worked.  Like my CC 2014 CCP pkgs, I set the RestartAction for the DPS Desktop Tools installer pkginfo to “RequireLogout”, so these installations took place at the Login Window.

However when I compared notes with other colleagues, those who had attempted to install DPS Desktop Tools had varied levels of success.  Usually the result of DPS Desktop Tools installation was a collegiate computer lab worth of failed installations and hung computers.  Further testing led me to discover that DPS Desktop Tools worked at the Login Window if run by Munki automatically, but not when installed by running “managedsoftwareupdate –installonly” or “managedsoftwareupdate –auto” via SSH.

Not wishing to spend large amounts of time tracking down random installation failures, I engaged Adobe support to help determine a reproducible way to install DPS Desktop Tools.  Through some gracious help examining why Munki inadvertently works (sometimes), it was determined that if the following conditions were met that DPS Desktop Tools could be reliably installed:

  • A user was logged into OS X during the installation (following the requirements of the support article)
  • A LaunchDaemon performed the installation (mimicking Munki’s use of a LaunchDaemon handling installation duties)

Adobe support provided with an unsupported installation method that installed a LaunchDaemon and used it to install DPS Desktop Tools.

Setting up Installation via Munki
The first step is to give Munki the ability to determine whether a user is logged in.  This is necessary since Munki normally attempts to install software when the computer is at the Login Window, but I’m specifically trying to prevent this behavior.  My solution was to write an Admin Provided Condition that checked to see if a user was logged in (further information regarding writing an Admin Provided Condition is available at Tim Sutton’s blog, in the article “Keeping your OS X VM guest tools current with Munki” under the heading “Writing an admin-provided condition”).  Leaning on a suggestion from Michael Lynn in ##osx-server IRC regarding the most appropriate way to determine if a user is logged in, I created this Admin Provided Condition:

#!/bin/bash

plistLocation=/Library/Managed\ Installs/ConditionalItems

#Verify a user is logged in
loggedInUser=`python -c 'from SystemConfiguration import SCDynamicStoreCopyConsoleUser; import sys; username = (SCDynamicStoreCopyConsoleUser(None, None, None) or [None])[0]; username = [username,""][username in [u"loginwindow", None, u""]]; sys.stdout.write(username + "\n");'`

if [ ! -z "${loggedInUser}" ]
then
  defaults write "${plistLocation}" isUserLoggedIn -bool true
else
  defaults write "${plistLocation}" isUserLoggedIn -bool false
fi

This script needs to be installed here (note only the directory is important–the script name itself could be anything):

/usr/local/munki/conditions/isUserLoggedInCondition.sh

Each time managedsoftwareupdate runs, the above script will be executed and the result of testing whether or not a user is logged in will be available for evaluation as a Conditional Item.  Thus adding an installable_condition such as the following to a Munki Installer Item’s pkginfo will detect whether a user is logged in*:

<key>installable_condition</key>
<string>isUserLoggedIn == TRUE</string>

Being Extra, Extra, Extra Sure a User is Logged In
*But will the installable_condition guarantee a user is logged in during installation?  Certainly the installable_condition works to prevent installation attempts when no one is logged in, but Managed Software Center collects all pending Installer Items and installs them together.  If one Installer Item requires a logout or a restart (i.e. its pkginfo declares a “RestartAction” of “RequireLogout”, “RequireRestart”, or “RequireShutdown”), Managed Software Center will log the user out and perform all installations at the Login Window.  This will make the DPS Desktop Tools installation fail, so additional checks are necessary to ensure a user is still logged in when DPS Desktop Tools installation occurs.  I added these additional checks in three places: the preinstall_script in the DPS Desktop Tools pkginfo and the preflight and postflight scripts for the DPS Desktop Tools pkg itself.  For the preinstall_script and the DPS Desktop Tools package preflight script:

#!/bin/bash

#If a user isn't logged in, fail
loggedInUser=`python -c 'from SystemConfiguration import SCDynamicStoreCopyConsoleUser; import sys; username = (SCDynamicStoreCopyConsoleUser(None, None, None) or [None])[0]; username = [username,""][username in [u"loginwindow", None, u""]]; sys.stdout.write(username + "\n");'`

if [ -z "${loggedInUser}" ]
then
  exit 1
fi

exit 0

The postflight script will be covered below.

Package Work
The structure of the DPS Desktop Tools installation package is the following:

  • Preflight
    • Verifies a user is logged in
  • Payloads
    • DPS Desktop Tools dmg
      /Users/Shared/AdobeDigitalPublishingPatch-CC2014-32.0.0.dmg
    • LaunchD job
      /Users/Shared/com.organization.dpsinstaller.plist
    • LaunchD-run installation script
      /Users/Shared/installDPS.sh
  • Postflight
    • Verifies a user is logged in
    • Loads LaunchD job and waits for the job to exit
      • LaunchD job runs DPS installation script
        • Mounts dmg
        • Installs DPS
        • Unmounts dmg
        • Unloads LaunchD job via launchctl unload
    • Deletes LaunchD-run installation script
    • Deletes LaunchD job file
    • Deletes DPS Desktop Tools dmg

The pieces of this installation:

  • Preflight (same as noted earlier, but here it is again)
    #!/bin/bash
    
    #If a user isn't logged in, fail
    loggedInUser=`python -c 'from SystemConfiguration import SCDynamicStoreCopyConsoleUser; import sys; username = (SCDynamicStoreCopyConsoleUser(None, None, None) or [None])[0]; username = [username,""][username in [u"loginwindow", None, u""]]; sys.stdout.write(username + "\n");'`
    
    if [ -z "${loggedInUser}" ]
    then
      exit 1
    fi
    
    exit 0
  • DPS Desktop Tools installer dmg
  • LaunchD job
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
    <plist version="1.0">
    <dict>
      <key>Label</key>
      <string>com.organization.dpsinstaller</string>
      <key>ProgramArguments</key>
      <array>
        <string>/Users/Shared/installDPS.sh</string>
      </array>
      <key>RunAtLoad</key>
      <true/>
    </dict>
    </plist>
  • Installation script run by LaunchD job
    #!/bin/bash
    
    pathToDmg=/Users/Shared/AdobeDigitalPublishingPatch-CC2014-32.0.0.dmg
    volumeName=AdobeDigitalPublishingCC2014-AdobeUpdate
    pathToAdobePatchInstaller=/AdobeDigitalPublishingCC2014-AdobeUpdate/AdobePatchInstaller.app/Contents/MacOS/AdobePatchInstaller
    pathToLaunchDJob=/Users/Shared/com.organization.dpsinstaller.plist
    
    #Mount the dmg
    /usr/bin/hdiutil attach "${pathToDmg}" -nobrowse
    
    #Install the update
    #It would be great to collect the exit status, but AdobePatchInstaller appears
    #to always exit 0
    /Volumes/"${volumeName}${pathToAdobePatchInstaller}" --mode=silent --skipProcessCheck=1
    
    #Eject the dmg
    /usr/bin/hdiutil detach /Volumes/"${volumeName}"
    
    #Unload the LaunchDaemon
    /bin/launchctl unload "${pathToLaunchDJob}"
  • Postflight (Note that since the postflight script runs after the package payloads have been installed, the postflight script goes through extra cleanup steps to remove those payloads if the check for a logged-in user fails)
    #!/bin/bash
    
    #Paths to important files
    pathToDPSInstallScript=/Users/Shared/installDPS.sh
    pathToLaunchDJob=/Users/Shared/com.organization.dpsinstaller.plist
    pathToDmg=/Users/Shared/AdobeDigitalPublishingPatch-CC2014-32.0.0.dmg
    
    loggedInUser=`python -c 'from SystemConfiguration import SCDynamicStoreCopyConsoleUser; import sys; username = (SCDynamicStoreCopyConsoleUser(None, None, None) or [None])[0]; username = [username,""][username in [u"loginwindow", None, u""]]; sys.stdout.write(username + "\n");'`
    
    #If a user isn't logged in, delete the payloads of the pkg and fail
    if [ -z "${loggedInUser}" ]
    then
      /bin/rm "${pathToDPSInstallScript}"
      /bin/rm "${pathToLaunchDJob}"
      /bin/rm "${pathToDmg}"
      exit 1
    fi
    
    #Determine the name of the LaunchD job
    launchDJobName=`/usr/bin/basename "${pathToLaunchDJob}" .plist`
    
    #Load the LaunchDaemon
    /bin/launchctl load "${pathToLaunchDJob}"
    
    #Wait for the LaunchDaemon to exit
    sleep 5
    while /bin/launchctl list | /usr/bin/grep -wq "${launchDJobName}"
    do
      sleep 5
    done
    
    #Delete the payloads of the pkg
    /bin/rm "${pathToDPSInstallScript}"
    /bin/rm "${pathToLaunchDJob}"
    /bin/rm "${pathToDmg}"
    
    #Exit cleanly. How else can we exit? The DPS installer does not exit with a status
    exit 0

Uninstallation
As discussed in my Distributing Adobe CC 2014 via Munki post, I’d like uninstallers for my Adobe CC 2014 applications.  In my testing, DPS Desktop Tools installs one InDesign plugin (/Applications/Adobe InDesign CC 2014/Plug-Ins/Graphics/Digital Publishing.InDesignPlugin) and two applications (/Applications/DPS App Builder.app and /Applications/Adobe/Adobe Content Viewer.app).  My testing also revealed that uninstalling InDesign CC 2014 removes the plugin, but leaves the applications installed.  Assuming I’m never going to remove the DPS Desktop Tools without also subsequently removing InDesign, a postuninstall_script of the following is sufficient:

#!/bin/bash

#Delete DPS App Builder and Adobe Content Viewer
/bin/rm -rf /Applications/DPS\ App\ Builder.app
/bin/rm -rf /Applications/Adobe/Adobe\ Content\ Viewer.app

exit 0

If removal of just DPS Desktop Tools is desired, it may be possible to add /Applications/Adobe InDesign CC 2014/Plug-Ins/Graphics/Digital Publishing.InDesignPlugin to the above list of files to delete, however this is untested so your mileage may vary.

Miscellaneous Topics
Applicability to other versions of DPS Desktop Tools
All of the testing here has been specific to the InDesign CC 2014 version of DPS Desktop Tools.  It’s unknown whether other versions of DPS Desktop Tools for InDesign CC or InDesign CS6 will encounter the same issues or if the same techniques will solve those issues.

DPS Desktop Tools as an optional_install
It may be tempting to add DPS Desktop Tools as an optional_install and forgo the preceding steps forcing installation only when a user is logged in.  However remember that once a user adds an optional_install, that updated versions of the Installer Item may be installed at any time the pkginfo allows.  So even if DPS Desktop Tools is just an optional_install, remember to follow the rest of the steps to prevent installation when a user isn’t logged in.

blocking_applications
The DPS Desktop Installer offers a list of blocking applications inside the mounted installer dmg:

/Volumes/AdobeDigitalPublishingCC2014-AdobeUpdate/AdobeDigitalPublishingCC2014-AdobeUpdate/payloads/UpdateManifest.xml

in the ConflictingProcesses section.  This list includes the following applications:

  • Adobe Content Viewer*
  • Adobe InDesign CS*
  • Creative Cloud Connection*
  • DPS App Builder*
  • InDesign CS*
  • InDesign*
  • Viewer Builder*

The asterisks (which are presumably wildcards) are part of the original list, implying that different software versions such as InDesign CS4 or InDesign CS5 could potentially interfere with a DPS Desktop Tools installation.  Also note that Munki’s blocking_applications currently does not support wildcards to match running applications.  It’s up to you to create a list of blocking_applications appropriate for your environment.

autoremove
In the spirit of my earlier post about Distributing Adobe CC 2014 via Munki, I set AutoRemove to “true” in the DPS Desktop Tools pkginfo.

installs Array
To help Munki intelligently examine the current installation of DPS Desktop Tools, an installs array is necessary.  This command when run against a computer with DPS Desktop Tools installed will generate an installs array for the three items installed:

makepkginfo -f /Applications/DPS App Builder.app -f /Applications/Adobe/Adobe Content Viewer.app -f /Applications/Adobe InDesign CC 2014/Plug-Ins/Graphics/Digital Publishing.InDesignPlugin

Copy the installs array and paste it into the pkginfo for DPS Desktop Tools.

update_for
If it’s desired to automatically install DPS Desktop Tools following every InDesign CC 2014 installation, make DPS Desktop Tools an update_for InDesign CC 2014.

requires
Since we’ve made DPS Desktop Tools dependent on an Admin Provided Condition, let’s require that Admin Provided condition.  I added the Installer Item “IsUserLoggedInCondition” to the “requires” array in the pkginfo for DPS Desktop Tools.

unattended_install
In order to get the best chance DPS Desktop Tools will be installed (since I’d prefer not to ask the user’s permission unless a blocking_application is running), I set unattended_install to true in the pkginfo for DPS Desktop Tools.

Example pkginfo
For review, here’s the entire pkginfo for the DPS Desktop Tools package created above:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
  <key>_metadata</key>
  <dict>
    <key>created_by</key>
    <string>admin</string>
    <key>creation_date</key>
    <date>2014-11-03T21:04:48Z</date>
    <key>munki_version</key>
    <string>2.0.0.2212</string>
    <key>os_version</key>
    <string>10.9.5</string>
  </dict>
  <key>autoremove</key>
  <true/>
  <key>catalogs</key>
  <array>
    <string>production</string>
  </array>
  <key>category</key>
  <string>Productivity</string>
  <key>description</key>
  <string>Digital publishing tools for InDesign CC 2014</string>
  <key>developer</key>
  <string>Adobe</string>
  <key>display_name</key>
  <string>DPS Desktop Tools CC2014</string>
  <key>installed_size</key>
  <integer>77825</integer>
  <key>installer_item_hash</key>
  <string>e5b30f7a5eee2470ad80b17149bd8f1df2f3225afdcf7b3427e3cf53fad1cbfd</string>
  <key>installer_item_location</key>
  <string>apps/adobe/DPS Desktop Tools CC2014-32.0.0.pkg</string>
  <key>installer_item_size</key>
  <integer>77645</integer>
  <key>minimum_os_version</key>
  <string>10.5.0</string>
  <key>name</key>
  <string>DPSDesktopToolsCC2014</string>
  <key>receipts</key>
  <array>
    <dict>
      <key>installed_size</key>
      <integer>77825</integer>
      <key>packageid</key>
      <string>com.organization.DPSDesktopToolsCC2014</string>
      <key>version</key>
      <string>32.0.0</string>
    </dict>
  </array>
  <key>uninstall_method</key>
  <string>removepackages</string>
  <key>uninstallable</key>
  <true/>
  <key>version</key>
  <string>32.0.0</string>
  <key>installs</key>
  <array>
    <dict>
      <key>CFBundleShortVersionString</key>
      <string>3.2.0</string>
      <key>path</key>
      <string>/Applications/DPS App Builder.app</string>
      <key>type</key>
      <string>application</string>
    </dict>
    <dict>
      <key>CFBundleShortVersionString</key>
      <string>3.4.3</string>
      <key>path</key>
      <string>/Applications/Adobe/Adobe Content Viewer.app</string>
      <key>type</key>
      <string>application</string>
    </dict>
    <dict>
      <key>CFBundleShortVersionString</key>
      <string>10.0.10.32</string>
      <key>path</key>
      <string>/Applications/Adobe InDesign CC 2014/Plug-Ins/Graphics/Digital Publishing.InDesignPlugin</string>
      <key>type</key>
      <string>bundle</string>
    </dict>
  </array>
  <key>blocking_applications</key>
  <array>
    <string>DPS App Builder</string>
    <string>Adobe Content Viewer</string>
    <string>Adobe InDesign CC 2014</string>
    <string>Creative Cloud Connection</string>
    <string>Adobe InDesign CS6</string>
    <string>Viewer Builder</string>
  </array>
  <key>preinstall_script</key>
  <string>#!/bin/bash

#If a user isn't logged in, fail
loggedInUser=`python -c 'from SystemConfiguration import SCDynamicStoreCopyConsoleUser; import sys; username = (SCDynamicStoreCopyConsoleUser(None, None, None) or [None])[0]; username = [username,""][username in [u"loginwindow", None, u""]]; sys.stdout.write(username + "\n");'`

if [ -z "${loggedInUser}" ]
then
   exit 1
fi

exit 0</string>
  <key>postuninstall_script</key>
  <string>#!/bin/bash

#Delete DPS App Builder and Adobe Content Viewer
/bin/rm -rf /Applications/DPS\ App\ Builder.app
/bin/rm -rf /Applications/Adobe/Adobe\ Content\ Viewer.app

exit 0</string>
  <key>installable_condition</key>
  <string>isUserLoggedIn == TRUE</string>
  <key>update_for</key>
  <array>
    <string>InDesignCC2014</string>
  </array>
  <key>requires</key>
  <array>
    <string>IsUserLoggedInCondition</string>
  </array>
  <key>unattended_install</key>
  <true/>
</dict>
</plist>

Epilogue
A technique like this should be avoided unless there’s no other way to get the software installed.  One possible issue would be that unless it’s the last installation in a chain of installations, adding an additional requirement to have a user logged in could have unusual effects with software installation dependencies (e.g. what happens if a piece of software that requires a restart depends on a piece of software that requires a user to be logged in?).  Not installing software at the Login Window is working against the design of Munki.

This is considerable effort to install a piece of software, and while it certainly seems to work there might be other issues I haven’t encountered yet.  If DPS Desktop Tools installation is something your organization is dependent on, you may wish to consider contacting your Adobe representative and sharing your opinions regarding the difficulty of installing DPS Desktop Tools.