{"id":1233,"date":"2022-09-30T09:56:17","date_gmt":"2022-09-30T14:56:17","guid":{"rendered":"https:\/\/www.brunerd.com\/blog\/?p=1233"},"modified":"2022-11-02T13:24:36","modified_gmt":"2022-11-02T18:24:36","slug":"determining-icloud-drive-and-desktop-and-documents-sync-status-in-macos","status":"publish","type":"post","link":"https:\/\/www.brunerd.com\/blog\/2022\/09\/30\/determining-icloud-drive-and-desktop-and-documents-sync-status-in-macos\/","title":{"rendered":"Determining iCloud Drive and Desktop and Documents Sync Status in macOS"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">I&#8217;m on a roll with <a href=\"https:\/\/www.brunerd.com\/blog\/2022\/09\/27\/determining-icloud-private-relay-and-limit-ip-tracking-status-in-macos\/\" target=\"_blank\" rel=\"noreferrer noopener\">iCloud stuff<\/a>. In this post I&#8217;d like to show you how you can determine if either iCloud Drive is enabled along with the &#8220;Desktop and Documents Folders&#8221; sync feature. While you can use MDM to turn these off, perhaps you like to know who you&#8217;d affect <em>first<\/em>! Perhaps the folks in your Enterprise currently using these features are in the C-Suite? I&#8217;m sure they&#8217;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).<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">iCloud Drive Status<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">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&#8217;s pretty straightforward but every now and then there&#8217;s something inscrutable and you have to take your best guess. For example iCloud Drive status is stored in your <strong>home<\/strong> folder at <code>~\/Library\/Preferences\/MobileMeAccounts.plist<\/code> but yet the Accounts key is an <strong>array<\/strong>. The question is how and why you could even have <em>more than one<\/em> 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. <\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Update:<\/strong> It seems quite obvious now but as many folks did point out, <em>of course<\/em> you can add another iCloud account to <strong>Internet Accounts<\/strong> and it can sync Mail, Contacts, Calendars, Reminders, and Notes, just <em>not <\/em>iCloud Drive. Only one iCloud Drive user per user on a Mac. While I <em>do<\/em> 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. <strong>Aside: <\/strong>Boy, do I wish I could merge or transfer my old iTunes purchasing\/media Apple ID with my main iCloud Apple ID! <code>&lt;weakly shakes fist at faceless Apple bureaucracy that for some reason hasn't solved the problem of merging Apple IDs><\/code><\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><a href=\"https:\/\/www.brunerd.com\/blog\/wp-content\/uploads\/AccountsArray-2.png\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"251\" src=\"https:\/\/www.brunerd.com\/blog\/wp-content\/uploads\/AccountsArray-2.png\" alt=\"\" class=\"wp-image-1267\" srcset=\"https:\/\/www.brunerd.com\/blog\/wp-content\/uploads\/AccountsArray-2.png 1024w, https:\/\/www.brunerd.com\/blog\/wp-content\/uploads\/AccountsArray-2-300x74.png 300w, https:\/\/www.brunerd.com\/blog\/wp-content\/uploads\/AccountsArray-2-768x188.png 768w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/a><\/figure>\n<\/div>\n\n\n<p class=\"wp-block-paragraph\"><s><strong>Regardless<\/strong> of that mystery<\/s>  <a rel=\"noreferrer noopener\" href=\"https:\/\/github.com\/brunerd\/jpt\" target=\"_blank\">jpt<\/a> can use the JSONPath query language to get us an answer in <a rel=\"noreferrer noopener\" href=\"https:\/\/github.com\/brunerd\/macAdminTools\/blob\/main\/Scripts\/iCloudDrive_func.sh\" target=\"_blank\">iCloudDrive_func.sh<\/a> and the minified <a rel=\"noreferrer noopener\" href=\"https:\/\/github.com\/brunerd\/macAdminTools\/blob\/main\/Scripts\/iCloudDrive_func.min.sh\" target=\"_blank\">iCloudDrive_func.min.sh<\/a>. Below is an edited excerpt: <\/p>\n\n\n\n<pre class=\"wp-block-code language-bash\"><code>#!\/bin\/bash\n#Joel Bruner - iCloudDrive.func.sh - gets the iCloud Drive status for a console user\n\n#############\n# FUNCTIONS #\n#############\n\nfunction iCloudDrive()(\n\n\t#for brevity pretend we've pasted in the minified jpt function:\n\t#<a href=\"https:\/\/github.com\/brunerd\/jpt\/blob\/main\/sources\/jpt.min\">https:\/\/github.com\/brunerd\/jpt\/blob\/main\/sources\/jpt.min<\/a>\n\t#for the full function see https:\/\/github.com\/brunerd\/macAdminTools\/tree\/main\/Scripts\n\n\tconsoleUser=$(stat -f %Su \/dev\/console)\n\n\t#if root grab the last console user\n\tif &#91; \"${consoleUser}\" = \"root\" ]; then\n\t\tconsoleUser=$(\/usr\/bin\/last -1 -t console | awk '{print $1}')\n\tfi\n\n\tuserPref_json=$(sudo -u $consoleUser defaults export MobileMeAccounts - | plutil -convert json - -o -)\n\n\t#pref domain not found an empty object is returned\n\tif &#91; \"${userPref_json}\" = \"{}\" ]; then\n\t\treturn 1\n\telse\n\t\t#returns the number paths that match\n\t\tmatchingPathCount=$(jpt -r '$.Accounts&#91;*].Services&#91;?(@.Name == \"MOBILE_DOCUMENTS\" &amp;&amp; @.Enabled == true)]' &lt;&lt;&lt; \"${userPref_json}\" 2&gt;\/dev\/null | wc -l | tr -d \"&#91;&#91;:space:]]\")\n\t\n\t\tif &#91; ${matchingPathCount:=0} -eq 0 ]; then\n\t\t\treturn 1\n\t\telse\n\t\t\treturn 0\n\t\tfi\n\tfi\n)\n########\n# MAIN #\n########\n\n#example function usage, if leverages the return values\nif iCloudDrive; then\n\techo \"iCloud Drive is ON\"\n\texit 0\nelse\n\techo \"iCloud Drive is OFF\"\n\texit 1\nfi<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The magic happens once we&#8217;ve gotten the JSON version of MobileMeAccount.plist I use jpt to see if there are any objects within the Accounts array with <code>Services<\/code> that have both a <code>Name<\/code> that matches <code>MOBILE_DOCUMENTS<\/code> <strong>and<\/strong> have an <code>Enabled<\/code> key that is set to <code>true<\/code>, the <code>-r<\/code> option on <code><a rel=\"noreferrer noopener\" href=\"https:\/\/github.com\/brunerd\/jpt\" target=\"_blank\">jpt<\/a><\/code> tells it to output the the JSON Pointer path(s) the query matches. I could have used the <code>-j<\/code> option to output JSONPath(s) but either way a line is a line and that&#8217;s all we need. Altogether it looks like this: <code>jpt -r '$.Accounts[*].Services[?(@.Name == \"MOBILE_DOCUMENTS\" &amp;&amp; @.Enabled == true)]<\/code><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Again because Accounts is an Array we have it look at <strong>all<\/strong> of them with <code>$.Accounts[*]<\/code>and in the off chance we get more than one we simply say if the number of matches is greater than zero then it&#8217;s on. This works very well in practice. This function could best be used as a JAMF Extension Attribute. I&#8217;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! \u23f2 And while some of you might balk at a 73k Extensions Attribute, the execution time is on average a speedy .15s!<\/p>\n\n\n\n<pre class=\"wp-block-code language-bash\"><code>#!\/bin\/bash\n#a pretend iCloudDrive Jamf EA \n#pretend we've pasted in the function iCloudDrive() above\n\nif iCloudDrive; then\n\tresult=\"ON\"\nelse\n\tresult=\"OFF\"\nfi\n\necho \"&lt;result&gt;${result}&lt;\/result&gt;\"<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">iCloud Drive &#8220;Desktop and Documents&#8221; status<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Thankfully, slightly easier to determine yet devilishly subtle to discover, is determining the status of the &#8220;Desktop and Documents&#8221; 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 <a rel=\"noreferrer noopener\" href=\"https:\/\/github.com\/brunerd\/macAdminTools\/blob\/main\/Scripts\/iCloudDriveDesktopSync_func.sh\" target=\"_blank\">iCloudDriveDesktopSync_func.sh<\/a> and the minified version <a rel=\"noreferrer noopener\" href=\"https:\/\/github.com\/brunerd\/macAdminTools\/blob\/main\/Scripts\/iCloudDriveDesktopSync_func%20min.sh\" target=\"_blank\">iCloudDriveDesktopSync_func min.sh<\/a><\/p>\n\n\n\n<pre class=\"wp-block-code language-bash\"><code>#!\/bin\/bash\n#Joel Bruner - iCloudDriveDesktopSync - gets the iCloud Drive Desktop and Document Sync Status for the console user\n\n#############\n# FUNCTIONS #\n#############\n\n#must be run as root\nfunction iCloudDriveDesktopSync()(\n\tconsoleUser=$(stat -f %Su \/dev\/console)\n\n\t#if root (loginwindow) grab the last console user\n\tif &#91; \"${consoleUser}\" = \"root\" ]; then\n\t\tconsoleUser=$(\/usr\/bin\/last -1 -t console | awk '{print $1}')\n\tfi\n\n\t#if this xattr exists then sync is turned on\n\txattr_desktop=$(sudo -u $consoleUser \/bin\/sh -c 'xattr -p com.apple.icloud.desktop ~\/Desktop 2&gt;\/dev\/null')\n\n\tif &#91; -z \"${xattr_desktop}\" ]; then\n\t\treturn 1\n\telse\n\t\treturn 0\n\tfi\n)\n\n#example function usage, if leverages the return values\nif iCloudDriveDesktopSync; then\n\techo \"iCloud Drive Desktop and Documents Sync is ON\"\n\texit 0\nelse\n\techo \"iCloud Drive Desktop and Documents Sync is OFF\"\n\texit 1\nfi<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The operation is pretty simple, it finds a console user or last user, then runs the <code>xattr -p<\/code> command as that user (anticipating this being run as root by Jamf) to see if the <code>com.apple.icloud.desktop<\/code> extended attribute exists on their <code>~\/Desktop<\/code>. In testing you&#8217;ll find if you toggle the &#8220;Desktop and Documents&#8221; 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. \ud83d\udcaa<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">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 <strong>but<\/strong> need to know <strong>who<\/strong> you are going to affect and let them know <em>beforehand<\/em>. Because I still stand by this sage advice: <a rel=\"noreferrer noopener\" href=\"https:\/\/www.brunerd.com\/blog\/2022\/03\/07\/respecting-focus-and-meeting-status-in-your-mac-scripts-aka-dont-be-a-jerk\/\" target=\"_blank\">Don&#8217;t Be A Jerk<\/a>. Thanks for reading you can find <a rel=\"noreferrer noopener\" href=\"https:\/\/github.com\/brunerd\/macAdminTools\/tree\/main\/Scripts\" target=\"_blank\">these script<\/a> and more at my <a href=\"https:\/\/github.com\/brunerd\" target=\"_blank\" rel=\"noreferrer noopener\">GitHub repo<\/a>. Thanks for reading!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>I&#8217;m on a roll with iCloud stuff. In this post I&#8217;d like to show you how you can determine if either iCloud Drive is enabled along with the &#8220;Desktop and Documents Folders&#8221; sync feature. While you can use MDM to turn these off, perhaps you like to know who you&#8217;d affect first! Perhaps the folks [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[3,19,37,51,12,39],"tags":[27,20,47,30,34,25,33,22,24],"class_list":["post-1233","post","type-post","status-publish","format-standard","hentry","category-apple","category-bash","category-jpt","category-json","category-scripting","category-zsh","tag-apple","tag-bash","tag-jamf","tag-jpt","tag-json","tag-macos","tag-os-x","tag-scripting","tag-shell"],"_links":{"self":[{"href":"https:\/\/www.brunerd.com\/blog\/wp-json\/wp\/v2\/posts\/1233","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.brunerd.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.brunerd.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.brunerd.com\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/www.brunerd.com\/blog\/wp-json\/wp\/v2\/comments?post=1233"}],"version-history":[{"count":18,"href":"https:\/\/www.brunerd.com\/blog\/wp-json\/wp\/v2\/posts\/1233\/revisions"}],"predecessor-version":[{"id":1268,"href":"https:\/\/www.brunerd.com\/blog\/wp-json\/wp\/v2\/posts\/1233\/revisions\/1268"}],"wp:attachment":[{"href":"https:\/\/www.brunerd.com\/blog\/wp-json\/wp\/v2\/media?parent=1233"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.brunerd.com\/blog\/wp-json\/wp\/v2\/categories?post=1233"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.brunerd.com\/blog\/wp-json\/wp\/v2\/tags?post=1233"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}