At work, we’re getting ready to roll out Adobe CC via Named User Licensing. In short, this means that users will install an Adobe CC Desktop Application (CCDA) that we provide, and log in with some Adobe credentials that we also provide (and manage). In this way, we can assign application licenses on an as-needed basis, instead of purchasing complete Adobe Suite licenses for all of our users.

We currently use Munki as our software distribution mechanism on macOS. Because our users have varying needs, we provide them with just the CCDA through Munki, and allow them to install the software they want/need. If they no longer need CCDA, they can just remove it through Munki as well. Sounds simple, right?

Not quite. As Adobe notes in their documentation,

The Creative Cloud desktop app can only be uninstalled if all Creative Cloud apps (such as Photoshop, Illustrator, and Premiere Pro) have already been uninstalled from the system.

Indeed, the results of attempting to do so are quite messy.

An obvious solution is to then inform users that they should uninstall all of their CC apps before trying to remove the CCDA. We plan on doing this, although Munki prompts are often ignored. So then, we consider a second possibility - can we remove the installed Adobe CC apps before removing the CCDA?

As it turns out, maybe! There are a couple of ways to uninstall applications once they’ve been installed. The most visible one is actually to use the uninstaller that comes with each app, e.g. for Photoshop 2021: /Applications/Adobe\ Photoshop\ 2021/Uninstall\ Adobe\ Photoshop\ 2021. These uninstallers are actually symlinks that link into /Library/Application\ Support/Adobe/Uninstall/, which is a centralised location where uninstallers for all of the installed applications seem to be kept.

Adobe also provides for a second, more centralised option. As far as I can tell, the only requirement for this method to work is that the CCDA is installed, which is ideal for us, as we want to run it before removing the CCDA using the uninstaller. This second option looks like this:

/Library/Application\ Support/Adobe/Adobe\ Desktop\ Common/HDBox/Setup \
--uninstall=1 --sapCode=PHSP --baseVersion=21.0 --platform=osx10-64 \
--deleteUserPreferences=false

You need to run this command with superuser privileges for it to work properly (which Munki does anyways), but it does what it says on the tin: removes Photoshop (PHSP) version 21.0, without removing any user preferences. There are, however, two caveats to this approach:

  1. You can’t just remove any and all applications in one go (probably for the best, as that might remove CCDA too). You need to know what you’re removing, and its corresponding “SAP code”.
  2. You have to specify a base version, which means you also have to be able to determine the version. You also can’t remove all versions at once, if the client has multiple versions installed.

Adobe helps us a little with both of these points. They have an article detailing the SAP codes and current base versions for each product.

Using this information

Remember that Uninstall directory I mentioned earlier? It turns out that all the items in there follow a naming convention, which is the applications SAP code along with its base version. As an example, Photoshop version 22.1 uses an uninstaller file named PHSP_22_1.app. This means that, for each CC app that the user has installed, we can get its SAP code, and its base version if we’re willing to parse that information. And, with that information, we can kick off the uninstaller mentioned previously.

So now we need to write something that:

  1. Checks which products are installed
  2. Checks which of these products are CC apps (i.e. not CCDA components)
  3. Removes each of these products, one by one

Armed with this knowledge, I wrote a little Python script that does the above (without removing the packages, yet).

import glob

ADOBE_UNINSTALL_DIR = '/Library/Application Support/Adobe/Uninstall/'

SAP_CODES = {
	'after_effects': 'AEFT',
	'animate': 'FLPR',
	'audition': 'AUDT',
	'bridge': 'KBRG',
	'character_animator': 'CHAR',
	'dimension': 'ESHR',
	'dreamweaver': 'DRWV',
	'illustrator': 'ILST',
	'incopy': 'AICY',
	'indesign': 'IDSN',
	'lightroom': 'LRCC',
	'lightroom_classic': 'LTRM',
	'media_encoder': 'AME',
	'photoshop': 'PHSP',
	'prelude': 'PRLD',
	'premiere_pro': 'PPRO',
	'premiere_rush': 'RUSH',
	'substance_alchemist': 'SBSTA',
	'substance_designer': 'SBSTD',
	'substance_painter': 'SBSTP',
	'xd': 'SPRK'
}

def parse(application):
	application = application.replace(ADOBE_UNINSTALL_DIR, '')
	application = application.replace('.app', '')
	application = application.split('_')
	version = '.'.join(application[1:])
	app_tuple = (application[0], version)
	return app_tuple

def search(sap_code):
	for code in SAP_CODES: 
		if sap_code == SAP_CODES[code]:
			return True
	return False

def main():
	apps = glob.glob(ADOBE_UNINSTALL_DIR + '*.app')
	uninstall_array = []
	for app in apps:
		app_info = parse(app)
		if(search(app_info[0])):
			print(f"Added {app_info[0]} with version {app_info[1]} to uninstall_array.")
			uninstall_array.append(app_info)
		
if __name__ == '__main__':
	main()

The script above will give the resulting output:

The Catch

As it turns out, some Adobe CC apps don’t allow us to get their base version in this way (most notably Lightroom Classic, Lightroom CC, and Adobe XD). In fact, these apps require you to use the base version listed on Adobe’s site, instead of the one that the app itself provides. As a result, for these apps we actually have no reliable way to remove them from the system without either hard-coding their base versions, or using an additional tool. This is frustrating, as this issue isn’t a problem with all Adobe CC apps - only a handful. After asking about this in the #adobe channel in the Mac Admins Slack, it was determined that this wasn’t something that could reliably done with the information that the CCDA leaves on the device. I’ve got a support ticket open with Adobe to see if there is a potential solution to this, or whether it would be possible to create a solution to this - any update on that will be posted here.

In the meantime, I’ve used the script above as an pre_uninstall check for the CCDA, where if the uninstall_array isn’t empty, uninstallation of the CCDA will be prevented. This script can be found on my Github.

Thanks for reading! Suggestions? Questions? Send me an email.