Filling the Gaps in MacOS Management

Filling the Gaps in MacOS Management

scripting, Tools
Filling the Gaps in MacOS Management

As I recently wrote in my widely popular article on evaluating Workspace ONE against Intune on MacOS, you can deliver effective MacOS management and design without JAMF. We all know that JAMF is the gold standard, but how can you close that gap? It’s not easy, but with some help you can secure, deliver, and excite users through creative engineering. We will embark on a journey beyond MDM profiles and how you can customize your deployment via scripts and automation.

Customizing MacOS Applications with Preferences

For those in the Windows world, we are very used to the idea of modifying the registry or pushing down Group Policy, which essentially writes entries to the registry. So how does this work on MacOS?

On a Mac, we have a section called “Preferences” which can be found in ~/Library/Preferences or /Library/Prefences. Basically, the ~/ signifies the user folder or things that run at the “user level” whereas the other path are running at the device level.

We have a few ways of looking at these files, such as using Finder or simply typing something like this in terminal:

defaults read

I would suggest taking a peak at the manual for the defaults command, which is pretty useful. The idea is you can print out the list of configurations for an application with that command, which looks like this:

Leveraging Preferences to Enhance the User Application Experience

We can use these preferences to build a great user experience and automate things that were manual processes in the past. It’s crucial to look deep into that.

Some of the scripts that I write will do one of two things:

  1. Copy a .plist file to the preferences folder
  2. Leverage defaults write to write values to the plist to deliver something that is not functionally available via MDM profiles.

In my GitHub, you will find many of those great scripts that build excellent user experiences via plist modification. A snip from one of my scripts can be seen below (full script is here):

/usr/bin/defaults write /Users/$loggedInUser/Library/Preferences/ AuthNegotiateDelegateWhitelist $tld
/bin/echo "AuthNegotiateDelegateWhitelist set to $tld for $loggedInUser"
/usr/bin/defaults write /Users/$loggedInUser/Library/Preferences/ AuthServerWhitelist $tld
/bin/echo "AuthServerWhitelist set to $tld for $loggedInUser"
/usr/bin/defaults write /Users/$loggedInUser/Library/Preferences/ AutoSelectCertificateForUrls -array-add -string "{\"pattern\":\"\",\"filter\":{}}"
/usr/sbin/chown $loggedInUser /Users/$loggedInUser/Library/Preferences/

Leveraging the functionality to build personalization within applications is very useful. Sometimes we will do it for user experience or to eliminate prompts/controls that build a poor user experience.

Customizing Core MacOS Functionality with Scripts

Similar to applications, we can also leverage a variety of scripts to build a great user experience. We will cover a few examples of how you can leverage scripting to elevate your deployment.

Setting the Default Mail Client to Outlook

An interesting item that I worked on recently, which was a bit challenging is customizing default file handlers. This is because LaunchServices. Launch Services is an Apple API that lets you run apps to open other apps or files e.g. launching apps, documents, understanding universal links and more.

This script below will write Outlook as the default mail client when you click on email links, try to open email files e.g. .eml files, calendar items, and contact cards. It’s a nice little basic python script that really gets the job done:


from LaunchServices import *

LSSetDefaultHandlerForURLScheme("mailto", "")
LSSetDefaultRoleHandlerForContentType("", kLSRolesAll, "")
LSSetDefaultRoleHandlerForContentType("public.vcard", kLSRolesAll, "")
LSSetDefaultRoleHandlerForContentType("", kLSRolesAll, "")


Customizing the MacOS Menu Bar

Another interesting area that you can leverage scripting is customizing the MacOS menu bar. This script below lets us write to the Apple System UI and enable several items, such as Bluetooth and Volume to improve the overall user experience and meet some baseline security requirements:

# Variables
User="$(defaults read '/Library/Application Support/AirWatch/Data/CustomAttributes/CustomAttributes' 'EnrollmentUser')"
/usr/bin/defaults write /Users/$User/Library/Preferences/ menuExtras -array \ "/System/Library/CoreServices/Menu Extras/" \
"/System/Library/CoreServices/Menu Extras/" \
"/System/Library/CoreServices/Menu Extras/" \
"/System/Library/CoreServices/Menu Extras/" \
"/System/Library/CoreServices/Menu Extras/" \
"/System/Library/CoreServices/Menu Extras/"
chmod 755 /Users/$User/Library/Preferences/

Deploying a Set of Printers on MacOS

Another area that I found to be severely lacking was printer management. This basic script below works wonders with removing the existing printer and repushing it, while pointing at the proper drivers. As you can see below, it’s straight forward and I’ve had very good success with it over the years:

/usr/sbin/lpadmin -x Printer01
/usr/sbin/lpadmin -p Printer01 -E -v lpd:// -o printer-is-shared=false -P /Library/Printers/PPDs/Contents/Resources/en.lproj/Xerox\ EX\ C60-C70\ Printer

Scripts vs. Applications on MacOS

I have often had a discussion around “should it be a script or should it be an app deployment?”

My experience has shown that people are far too rigid on this trying to make every single thing run through an app store. Holistically, I try to keep utilities/agents as scripted deployments and keep user-facing technologies as apps.

The synergy you get with a collection of scripts leveraging dependencies can be amazing. A few great real-world examples are deploying Splunk or an agent like Crowdstrike, Nessus, Microsoft Defender ATP, etc.

Deploying Splunk on MacOS

When deploying Splunk, we run a few scripts to make it happen. First, we deploy a script to create the user account and add it to the groups needed to run the agent:

#Create Splunk User
sudo sysadminctl -addUser splunk -fullName "Splunk" -password PASSWORD
#Create Splunk Group
sudo dscl . create /Groups/splunk

Once that script runs successfully, a second script runs to deploy and setup Splunk appropriately:

cd /opt/;export SPLUNK_HOME=/opt/splunkforwarder
sudo tar xvzf /tmp/splunkforwarder*.tgz
cd ./splunkforwarder/

#Kill splunkd if running
PID=$(pgrep splunkd)

if [ ! -z "$PID" ]
        killall splunkd

#General Deployment Client App#

# Splunk First-Time-Run (FTR)

sleep 3
# NOTE: This part MUST BE RUN AS root (or sudo)!
# NOTE: If running splunk as non-root, add "-user splunk" to the argument list of "enable boot-start"
sudo cp /usr/local/user-seed.conf "${SPLUNK_HOME}"/etc/system/local/user-seed.conf
sudo cp /usr/local/deploymentclient.conf "${SPLUNK_HOME}"/etc/system/local/deploymentclient.conf
sudo chown -R splunk ${SPLUNK_HOME}
sudo -u splunk ${SPLUNK_HOME}/bin/splunk start --accept-license --auto-ports --no-prompt --answer-yes
sudo "${SPLUNK_HOME}"/bin/splunk enable boot-start -user splunk

sleep 3

sudo launchctl load /Library/LaunchDaemons/com.splunk.plist

# Ownership probably does not need to be changed because
# probably script is being run as correct user which may be root or splunk or other.

Simply, this deployment is elegant and effective which may have not been completely feasible/possible as an application. Sometimes we just find that application deployments run better as scripts. We must always be flexible and shift as needed.

Final Thoughts

I will say that this article is a bit shorter than most of the stuff that I write, but I don’t think you need to drag this out too much.

Simply, you are NOT an architect if you think everything is one size fits all. You’re an administrator

An architect looks at the situation and weaves a web of magnificence to solve a business problem. Some things can be done via profiles, but sometimes you need to be a bit creative and write some Python or some Bash to make something happen. Architects are not discouraged by a lack of documentation, but they live for the problem. Solutioning something that people haven’t figured out yet is truly the most rewarding aspect to the Modern Workplace.



Social Media

Get The Latest Updates

Subscribe To Our Weekly Newsletter

No spam, notifications only about the latest posts and updates.