
When Apple finally released the API for Apple Business & School Manager last year I was thrilled. I took it as an opportunity to try and create a Swift CLI for it which ended up being asbmutil, a Swift binary that directly communicates with the Apple School & Business Manager API. I posted it in the MacAdmins Slack, didn't think much about it, and it ended up getting used by folks — along with requests to make it better.
Today I'm shipping ASBMUtil.app, a native SwiftUI front-end on top of the same engine. Same credentials store, same API client, same bulk operations — inside a native Liquid Glass Tahoe app.

Not every Mac admin wants to live in the terminal. Even a well-built CLI carries its own friction — "here's the command, make sure the profile is set, redirect jq somewhere, don't forget the –mdm flag…"
Hence the GUI app. Same engine underneath, same credentials store, same API client.

The main view is a single native table with every managed device in your Apple Business & School Manager account — Macs, iPads, iPhones, Apple TVs, all of it — right next to each other. Click a row and a sidebar slides in with the full device details: serial, order, status, MDM assignment, AppleCare coverage, MAC addresses, the works. Select a handful, right-click, reassign.


The filters match the web — same categories, same stacking behaviour — so if you already use the ABM filters, you already know how these work. The difference is that here they run against a native table with every device already loaded, so stacking is instant, the selection carries into bulk assign/unassign in one click, and the filtered set exports to CSV or JSON through the share sheet.
Filter by order number, device status, model family, or MDM server — and stack them, so "unassigned iPads from the last PO that haven't been routed to Intune yet" is three clicks.


Once you've narrowed to the right cohort, export (CSV/JSON) or bulk-reassign works on the filtered selection — not on the whole fleet.
–profile flags.StrictConcurrency enabled — actor-isolated, race-free by design.list-devices –include-applecare runs a two-pass fan-out across the whole fleet, no CSV required.I've kept the tool caught up with the AxM API as Apple has shipped new fields:
get-devices-info, whole-fleet via list-devices –include-applecare).Alongside the GUI app, the other side of the coin — fully cloud, headless operations — has gotten a lot of work too. Fully static Swift runtime via –static-swift-stdlib, URLSession fixes for musl, and keychain-less credential provisioning from env vars so the same codebase runs cleanly on Ubuntu. One Swift 6 binary, one data model, one API client — running unchanged inside Azure Functions, AWS Lambda, or any Linux container.
Cloud automations that the Linux binary offers:
asbmutil what Apple Business & School Manager currently shows, diffs the two, and issues assign/unassign calls to close the gap. Anything the inventory system considers authoritative — MDM server, department, ownership tags — can drive the comparison.asbmutil, hashes the normalised payload per serial, stores the fingerprint in blob storage, and only writes back to inventory when the Apple-side data has actually changed. Warranty months, coverage status, order number, MDM assignment — all flow in automatically, and the hash cache keeps API traffic and downstream write-load proportional to real change, not fleet size.Outcome: inventory and Apple Business & School Manager stay in lockstep without anyone opening either UI. Warranty data flows in one direction, MDM assignments in the other, both driven by the same Swift binary you'd run on your Mac.
I plan to write in more detail how I'm running these and the code behind them in follow-up posts.
The CLI has its place. It's the thing I'll use to check which MDM a device is assigned to when I need quick operations — asbmutil list-devices-servers –mdm "Intune" piped through jq and grep, done in a second.
For bulk operations I reach for the GUI app — load a CSV, select in the table, hit the button. Being able to see what's assigned, what's unassigned, and peruse the fleet side-by-side catches mistakes before they ship and surfaces clean-up opportunities you'd miss on the command line.
And for the continuous, unattended work — nightly syncs, warranty enrichment, inventory reconciliation — the Linux binary inside a serverless function quietly does it in the background, no interaction, automated.
Same data model, same trust store, same API calls underneath.

Repo: github.com/rodchristiansen/asbmutil
Grab the latest .pkg or .dmg from the releases page. The installer drops both the app into /Applications and the asbmutil CLI into /usr/local/bin.
A note on signing: the released artifacts are unsigned and not notarized. On first launch Gatekeeper will complain — clear the quarantine attribute and you're good:
xattr -dr com.apple.quarantine /Applications/ASBMUtil.app
xattr -dr com.apple.quarantine /usr/local/bin/asbmutil
Or build and sign it yourself from source — make build in the repo will do the whole signing + notarization dance if you drop your own Developer ID into .env. As Mac admins you know the drill.
Requirements: macOS 14+, and an AxM API account with a client ID, key ID, and PEM. If you already have the CLI configured, the app reads the same keychain profiles — nothing to migrate.
More posts coming on the specific things this tool makes trivial that ABM's web UI makes painful — starting with the bulk device-to-server resolver, which I think is the most interesting piece of the codebase. For now: v1 of the app exists, it works, and I'd love to hear where it breaks for you.