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 console → Devices → Add Device → Managed (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:
- Run Script — copy scripts to the Mac, run
dohzel-mdm-enroll.shwith your tokens. - Run Script — run
dohzel-mdm-provision.sh. - 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:
- Enrolls (or reuses existing enrollment from
/var/db/dohzel/enrollment.json) - Downloads
/var/db/dohzel/dohzel-dns.mobileconfig - 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 |
Related documentation#
- MDM overview
- Microsoft Intune — Windows
- DoHzel Proxy installation (Linux) — similar enroll / join concepts for proxy