Skip to content

Deploy Lander with Intune

This guide rolls Lander out to a managed macOS fleet with Microsoft Intune. The pattern is the same for any MDM: ship a signed package, inject the control-plane settings, and run the service install. See Jamf Pro and Kandji for the equivalents.

flowchart LR
  Pkg["Signed .pkg\n(Lander binary)"] --> Wrap[".intunemac\n(wrapped)"]
  Wrap --> Intune["Intune\nmacOS app"]
  Settings["LANDER_ENV_MANAGER_URL"] --> Script["Shell script\n/ LaunchAgent"]
  Intune --> Devices["Device group"]
  Script --> Devices
  Devices --> Register["lander service install\n→ Lander prompts user\n→ registers in fleet"]

Produce a flat macOS installer package that places the binary on PATH and create a LaunchAgent.

  1. Stage the binary at /usr/local/bin/lander.
  2. Build a component package with pkgbuild and sign it with your Developer ID Installer certificate.
  3. Notarize the package so Gatekeeper allows it on managed Macs.
Terminal window
pkgbuild --root ./payload \
--identifier com.aliengiraffe.lander \
--version 1.0.0 \
--install-location / \
lander-unsigned.pkg
productsign --sign "Developer ID Installer: Your Org" \
lander-unsigned.pkg lander.pkg
# then notarize lander.pkg with notarytool

Convert the signed .pkg into a .intunemac with the Intune App Wrapping Tool for macOS (IntuneAppUtil):

Terminal window
IntuneAppUtil -c lander.pkg -o ./out

Upload the resulting .intunemac in the Intune admin center under Apps → macOS → Add → macOS app (PKG).

Endpoints need one setting: the control-plane URL. Deliver it with an Intune shell script (Devices → Scripts) that writes a LaunchAgent and runs the service install. There is no owner email to map — the device is attributed to whoever authenticates at Lander’s prompt.

#!/bin/bash
set -euo pipefail
LANDER_ENV_MANAGER_URL="https://control-plane.example.com/v1"
CONSOLE_USER=$(stat -f%Su /dev/console)
PLIST="/Users/${CONSOLE_USER}/Library/LaunchAgents/com.aliengiraffe.lander.env.plist"
mkdir -p "$(dirname "$PLIST")"
cat > "$PLIST" <<EOF
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key><string>com.aliengiraffe.lander.env</string>
<key>EnvironmentVariables</key>
<dict>
<key>LANDER_ENV_MANAGER_URL</key><string>${LANDER_ENV_MANAGER_URL}</string>
</dict>
<key>ProgramArguments</key>
<array><string>/usr/local/bin/lander</string><string>service</string><string>install</string><string>--port</string><string>8082</string></array>
<key>RunAtLoad</key><true/>
</dict>
</plist>
EOF
chown "${CONSOLE_USER}" "$PLIST"
sudo -u "${CONSOLE_USER}" launchctl bootstrap gui/$(id -u "${CONSOLE_USER}") "$PLIST" || true

Run the script as root and sudo -u into the console user as shown, so the LaunchAgent lands in that user’s GUI session. That session is what lets Lander open the sign-in browser itself — a service installed outside a GUI session never prompts, and the device will not enroll.

Assign both the app and the script to the target device group (or user group). Intune installs the package and runs the script; Lander installs its launchd service, prompts its user to authenticate, and then registers the device and pushes inventory.

  • In Intune, confirm the app shows Installed and the script reports success for the device.
  • On a sample device: lander service status and lander device status show the service running and a deviceId assigned. If there is no deviceId, check lander auth status — an unauthenticated device installs cleanly but does not enroll, and the daemon re-checks every 15 minutes without intervention.
  • In the control-plane dashboard, the devices appear under Fleet → Devices (/admin/fleet/devices) as their users authenticate, each owned by the account that did. Expect the count to trail the install count during a rollout.
  • Update — upload a new .intunemac version; Intune replaces the binary. The launchd KeepAlive restarts the service on the new binary.
  • Remove — deliver a script that runs lander service uninstall, and unassign the app.

Next: see the data flowing in Monitor Agents & MCPs.