Determining iCloud Drive and Desktop and Documents Sync Status in macOS

I’m on a roll with iCloud stuff. In this post I’d like to show you how you can determine if either iCloud Drive is enabled along with the “Desktop and Documents Folders” sync feature. While you can use MDM to turn these off, perhaps you like to know who you’d affect first! Perhaps the folks in your Enterprise currently using these features are in the C-Suite? I’m sure they’d appreciate a heads up before all their iCloud docs get removed from their Macs (when MDM disallowance takes affect it is swift and unforgiving).

iCloud Drive Status

When it comes to using on-disk artifacts to figure out the state of macOS I like to use the analogy of reading tea leaves. Usually it’s pretty straightforward but every now and then there’s something inscrutable and you have to take your best guess. For example iCloud Drive status is stored in your home folder at ~/Library/Preferences/MobileMeAccounts.plist but yet the Accounts key is an array. The question is how and why you could even have more than one iCloud account signed-in?! Perhaps a reader will tell me when and how you would ever have more than one? For now it seems inexplicable.

Update: It seems quite obvious now but as many folks did point out, of course you can add another iCloud account to Internet Accounts and it can sync Mail, Contacts, Calendars, Reminders, and Notes, just not iCloud Drive. Only one iCloud Drive user per user on a Mac. While I do have another AppleID I only use it for Media and Purchases and none of the other iCloud services. The code below stays the same as it is looking at all array entries. Aside: Boy, do I wish I could merge or transfer my old iTunes purchasing/media Apple ID with my main iCloud Apple ID! <weakly shakes fist at faceless Apple bureaucracy that for some reason hasn't solved the problem of merging Apple IDs>

Regardless of that mystery jpt can use the JSONPath query language to get us an answer in iCloudDrive_func.sh and the minified iCloudDrive_func.min.sh. Below is an edited excerpt:

#!/bin/bash
#Joel Bruner - iCloudDrive.func.sh - gets the iCloud Drive status for a console user

#############
# FUNCTIONS #
#############

function iCloudDrive()(

	#for brevity pretend we've pasted in the minified jpt function:
	#https://github.com/brunerd/jpt/blob/main/sources/jpt.min
	#for the full function see https://github.com/brunerd/macAdminTools/tree/main/Scripts

	consoleUser=$(stat -f %Su /dev/console)

	#if root grab the last console user
	if [ "${consoleUser}" = "root" ]; then
		consoleUser=$(/usr/bin/last -1 -t console | awk '{print $1}')
	fi

	userPref_json=$(sudo -u $consoleUser defaults export MobileMeAccounts - | plutil -convert json - -o -)

	#pref domain not found an empty object is returned
	if [ "${userPref_json}" = "{}" ]; then
		return 1
	else
		#returns the number paths that match
		matchingPathCount=$(jpt -r '$.Accounts[*].Services[?(@.Name == "MOBILE_DOCUMENTS" && @.Enabled == true)]' <<< "${userPref_json}" 2>/dev/null | wc -l | tr -d "[[:space:]]")
	
		if [ ${matchingPathCount:=0} -eq 0 ]; then
			return 1
		else
			return 0
		fi
	fi
)
########
# MAIN #
########

#example function usage, if leverages the return values
if iCloudDrive; then
	echo "iCloud Drive is ON"
	exit 0
else
	echo "iCloud Drive is OFF"
	exit 1
fi

The magic happens once we’ve gotten the JSON version of MobileMeAccount.plist I use jpt to see if there are any objects within the Accounts array with Services that have both a Name that matches MOBILE_DOCUMENTS and have an Enabled key that is set to true, the -r option on jpt tells it to output the the JSON Pointer path(s) the query matches. I could have used the -j option to output JSONPath(s) but either way a line is a line and that’s all we need. Altogether it looks like this: jpt -r '$.Accounts[*].Services[?(@.Name == "MOBILE_DOCUMENTS" && @.Enabled == true)]

Again because Accounts is an Array we have it look at all of them with $.Accounts[*]and in the off chance we get more than one we simply say if the number of matches is greater than zero then it’s on. This works very well in practice. This function could best be used as a JAMF Extension Attribute. I’ll leave that as a copy/paste exercise for the reader. Add it to your Jamf Pro EAs, let sit for 24-48 hours and check for results! ⏲ And while some of you might balk at a 73k Extensions Attribute, the execution time is on average a speedy .15s!

#!/bin/bash
#a pretend iCloudDrive Jamf EA 
#pretend we've pasted in the function iCloudDrive() above

if iCloudDrive; then
	result="ON"
else
	result="OFF"
fi

echo "<result>${result}</result>"

iCloud Drive “Desktop and Documents” status

Thankfully, slightly easier to determine yet devilishly subtle to discover, is determining the status of the “Desktop and Documents” sync feature of iCloud Drive. After searching in vain for plist artifacts, I discovered the clue is in the extended attributes of your Desktop (and/or Documents) folder! You can find the scripts at my GitHub here iCloudDriveDesktopSync_func.sh and the minified version iCloudDriveDesktopSync_func min.sh

#!/bin/bash
#Joel Bruner - iCloudDriveDesktopSync - gets the iCloud Drive Desktop and Document Sync Status for the console user

#############
# FUNCTIONS #
#############

#must be run as root
function iCloudDriveDesktopSync()(
	consoleUser=$(stat -f %Su /dev/console)

	#if root (loginwindow) grab the last console user
	if [ "${consoleUser}" = "root" ]; then
		consoleUser=$(/usr/bin/last -1 -t console | awk '{print $1}')
	fi

	#if this xattr exists then sync is turned on
	xattr_desktop=$(sudo -u $consoleUser /bin/sh -c 'xattr -p com.apple.icloud.desktop ~/Desktop 2>/dev/null')

	if [ -z "${xattr_desktop}" ]; then
		return 1
	else
		return 0
	fi
)

#example function usage, if leverages the return values
if iCloudDriveDesktopSync; then
	echo "iCloud Drive Desktop and Documents Sync is ON"
	exit 0
else
	echo "iCloud Drive Desktop and Documents Sync is OFF"
	exit 1
fi

The operation is pretty simple, it finds a console user or last user, then runs the xattr -p command as that user (anticipating this being run as root by Jamf) to see if the com.apple.icloud.desktop extended attribute exists on their ~/Desktop. In testing you’ll find if you toggle the “Desktop and Documents” checkbox in the iCloud Drive options, it will apply this to both of those folders almost immediately without fail. The function can be used in a Jamf Extension Attribute in the same way the iCloudDrive was above. Some assembly required. 💪

So, there you go! Another couple functions to read the stateful tea leaves of iCloud Drive settings. Very useful if you are about to disallow iCloud Drive and/or Desktop and Document sync via MDM but need to know who you are going to affect and let them know beforehand. Because I still stand by this sage advice: Don’t Be A Jerk. Thanks for reading you can find these script and more at my GitHub repo. Thanks for reading!