DoHzel macOS MDM enrollment#

Introduction#

This guide describes how to enroll Mac computers in DoHzel using Mobile Device Management (MDM). The scripts rely only on bash, curl, and plutil — all built into macOS.

Two workflows are available:

Option Scripts Profile installation
A — Split (recommended for MDM at scale) dohzel-mdm-enroll.sh + dohzel-mdm-provision.sh Your MDM installs the .mobileconfig (Intune, Kandji, Jamf, …)
B — Standalone (single run script) dohzel-mdm-standalone.sh Script downloads and installs the profile on the Mac

Option A avoids macOS 13+ limitations on manual profile installation and is the preferred pattern for fleet deployment. Option B is useful for pilots, one-off Macs, or MDM policies that only support a single run script.

Prerequisites#

On each managed Mac:

  • curl — included with macOS (/usr/bin/curl)
  • plutil — included with macOS (used to read JSON; part of the base system)
  • Outbound HTTPS to api.hafnova.com
  • A valid context token (from Hafnova) and join token (from the DoHzel console)

Scripts#

Each script is self-contained — download only the file(s) you need. After download, make them executable: chmod +x /usr/local/dohzel/*.sh.

Script Role
dohzel-mdm-enroll.sh Option A — step 1: enroll
dohzel-mdm-provision.sh Option A — step 2: download mobileconfig
dohzel-mdm-standalone.sh Option B — enroll, download, and install

Tokens#

Two tokens are required for enrollment. They are passed on the command line (or via MDM script parameters). Do not hard-code production tokens in public documentation.

Token Script flag Purpose Where to obtain
Context token --context-token Identifies the DoHzel tenant when registering the device with the Hafnova enrollment API Hafnova — Contact us
Join token --join-token Links the device to a DoHzel profile as Managed or Freedom DoHzel consoleDevicesAdd DeviceManaged (or Freedom if the device should join the profile as a Freedom device)

Option A — Split workflow (MDM installs profile)#

Step 1 — Enroll the device#

Run as root from your MDM “Run Script” policy:

/usr/local/dohzel/dohzel-mdm-enroll.sh \
  --context-token "YOUR_CONTEXT_TOKEN" \
  --join-token "YOUR_JOIN_TOKEN" \
  --device-name "MacBook-Sales-01"

Optional flags:

Flag Description
--api-base URL Default: https://api.hafnova.com/api
--state-dir DIR Default: /var/db/dohzel
--force Create a new device even if already enrolled

On success, state is saved to /var/db/dohzel/enrollment.json. Re-running without --force exits successfully if the device is already enrolled.

Step 2 — Download the mobileconfig#

/usr/local/dohzel/dohzel-mdm-provision.sh

Defaults:

  • Reads the device token from /var/db/dohzel/enrollment.json
  • Writes /var/db/dohzel/dohzel-dns.mobileconfig
  • Prints the output path on stdout

Step 3 — Deploy via MDM#

Install **dohzel-dns.mobileconfig** using your MDM’s configuration profile channel:

MDM Typical approach
Microsoft Intune Settings catalog / custom configuration profile; upload .mobileconfig
Kandji Library → Custom Profile → upload .mobileconfig
Jamf Pro Configuration Profiles → upload .mobileconfig
JumpCloud Policy → Custom Configuration Profile

Recommended policy order:

  1. Run Script — copy scripts to the Mac, run dohzel-mdm-enroll.sh with your tokens.
  2. Run Script — run dohzel-mdm-provision.sh.
  3. Configuration Profile — install the generated dohzel-dns.mobileconfig.

Full example (split, MDM run script)#

#!/bin/bash
set -euo pipefail

SCRIPT_DIR="/usr/local/dohzel"
CONTEXT_TOKEN="YOUR_CONTEXT_TOKEN"
JOIN_TOKEN="YOUR_JOIN_TOKEN"

"${SCRIPT_DIR}/dohzel-mdm-enroll.sh" \
  --context-token "${CONTEXT_TOKEN}" \
  --join-token "${JOIN_TOKEN}"

PROFILE="$("${SCRIPT_DIR}/dohzel-mdm-provision.sh")"
echo "Mobileconfig ready at: ${PROFILE}"
# MDM follow-up policy installs ${PROFILE} as a configuration profile

Option B — Standalone workflow (script installs profile)#

One script enrolls the Mac, downloads the signed DNS mobileconfig, and installs it locally.

/usr/local/dohzel/dohzel-mdm-standalone.sh \
  --context-token "YOUR_CONTEXT_TOKEN" \
  --join-token "YOUR_JOIN_TOKEN" \
  --device-name "MacBook-Sales-01"

What it does:

  1. Enrolls (or reuses existing enrollment from /var/db/dohzel/enrollment.json)
  2. Downloads /var/db/dohzel/dohzel-dns.mobileconfig
  3. Installs the profile via profiles install, or on macOS 13+ opens System Settings so the user can approve it

Optional flags:

Flag Description
--no-install Download only; skip profiles install / System Settings
--force Enroll a new device before downloading
--output PATH Custom mobileconfig path
--state-dir DIR Custom state directory

If the device is already enrolled, the script re-downloads and re-installs the profile (refresh) without re-enrolling, unless --force is set.

Full example (standalone, MDM run script)#

#!/bin/bash
set -euo pipefail

SCRIPT_DIR="/usr/local/dohzel"
CONTEXT_TOKEN="YOUR_CONTEXT_TOKEN"
JOIN_TOKEN="YOUR_JOIN_TOKEN"

"${SCRIPT_DIR}/dohzel-mdm-standalone.sh" \
  --context-token "${CONTEXT_TOKEN}" \
  --join-token "${JOIN_TOKEN}"

macOS 13+ profile installation

On recent macOS versions, profiles install may not work for user-approved profiles. The standalone script falls back to open on the .mobileconfig, which queues installation in System Settings → Privacy & Security → Profiles. For unattended fleet rollout, prefer Option A and push the profile through MDM.


Troubleshooting#

Symptom Likely cause
Invalid context Wrong or rotated context token (from Hafnova)
Invalid code Wrong or expired join token (from DoHzel console)
HTML in mobileconfig file Bad device token or API URL
mobileconfig download failed Re-enroll with --force or check network
Profile not active after standalone run macOS 13+ requires user approval in System Settings, or use Option A