Mosyle 101: Why is my Custom Command not working?

If you’re just starting out using Mosyle MDM, you may have run into a situation where a Custom Command that you’ve sent to a device doesn’t appear to work. You test the script on your local machine, and subsequently tear your hair out when it works just fine.

Cause #1: You’re not running the command as the local user

MDM Custom Commands are, by default, run on target devices as the root user, and not the local user.

For example, if you want to create a script that disables Natural scrolling on the user’s Magic Trackpad, simply running the following command in a Custom Command profile won’t work:

defaults write NSGlobalDomain com.apple.swipescrolldirection -bool false

Because Custom Commands are executed by the Mosyle MDM agent as the root user, you’re only modifying the scrolling preferences for the root user, and not the currently logged in user.

The simple solution for this is to run the command via sudo, using the –u flag to instruct sudo to run the command as the currently logged in user (stat -f “%Su” /dev/console).

sudo -u $(stat -f "%Su" /dev/console) /bin/sh <<'END'

defaults write NSGlobalDomain com.apple.swipescrolldirection -bool false

Add this sudo line to the top of your script, and all commands which follow that line will be run as the current user, instead of root.

Cause #2: Unqualified paths to executables

The other reason why your scripts may be failing is because you’re calling executables that are installed in locations that aren’t in the user’s $PATH.

Let’s say you want to run a script that uses the awesome JSON utility jq. If the script calls the jq binary without a fully qualified path to that binary (i.e. /usr/local/bin/jq) it will work fine on your local system, because /usr/local/bin is likely in your $PATH.

The root user, however, does not have /usr/local/bin specified in its default $PATH. So calling jq will result in a “command not found” error if you try to run the same script on an MDM managed system.

The simplest solution to this is to assign the full path to the executables to variables at the top of your script, then use the variables via parameter substitution whenever you need call that executable from within the script.

#!/usr/bin/env bash

# Assign fully-qualified path to jq to variable
jq="/usr/local/bin/jq"

# Return jq version number
"$jq" --version