Auto-assigning Macs to a Jamf site from an Entra ID smart group
Jamf sites are static and smart groups don't move records into them. Here's a scheduled routine that reads an Entra-tied smart group and assigns each member to the right site through the API.
I wanted something that sounds like it should be a checkbox: Macs whose user belongs to a particular Entra ID group should land in a specific Jamf site, automatically, so the regional admins only see their own machines. It isn’t a checkbox. A Jamf site is a static attribute on the computer record, and smart groups don’t move records into sites — they scope policies and profiles, nothing more. So “automatically added to a site” really means: something runs on a schedule, reads the group’s members, and writes the site onto each one through the API.
Here’s the routine I use to do exactly that.
Step 1: the Entra-tied smart group
First you need a smart group whose membership reflects an Entra ID group. Two ways to get there — confirm which one matches your setup:
- Jamf connected to Entra as a cloud identity provider, with an assigned user on each computer. The smart group criterion then keys off the assigned user’s Entra group membership. This is the cleanest path if you’ve already wired Jamf to Entra.
- An extension attribute that surfaces the user’s Entra group — populated by a script (a Microsoft Graph lookup, or reading what Platform SSO / the Company Portal already knows locally). The smart group criterion keys off that EA’s value.
Either way you end up with a smart group — say “Entra – Marketing” — that fills and empties as people move in and out of the Entra group. Note its group ID (it’s in the URL when you open the group in Jamf). That ID is the only handle the routine needs.
Step 2: why this needs a routine at all
Worth saying plainly, because it’s the thing that trips people up: there is no native “members of this smart group go in this site” setting. Site is assigned per-record, by hand or by API. So the job is a small loop — get the group’s members, PUT the site onto each — that you run on a schedule. That loop is the “routine.”
Step 3: the routine
A bash version you can drop into cron, a LaunchDaemon, or an Azure Function:
#!/bin/bash
# Sync Jamf site assignment from an Entra-tied smart group.
# Run on a schedule. Idempotent — re-running just re-asserts the same state.
set -euo pipefail
JAMF_URL="https://yourorg.jamfcloud.com"
CLIENT_ID="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
CLIENT_SECRET="…" # from an API Role/Client (see privileges below)
SMART_GROUP_ID="42" # the Entra-tied smart group
TARGET_SITE="Marketing" # the Jamf site to drop members into
# --- 1. bearer token (modern OAuth client-credentials) ---------------------
token=$(curl -fsS -X POST "$JAMF_URL/api/oauth/token" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "client_id=$CLIENT_ID" \
-d "client_secret=$CLIENT_SECRET" \
-d "grant_type=client_credentials" \
| plutil -extract access_token raw -)
auth=(-H "Authorization: Bearer $token")
# --- 2. read smart group membership (Classic API — bearer works here) ------
ids=$(curl -fsS "${auth[@]}" -H "Accept: application/xml" \
"$JAMF_URL/JSSResource/computergroups/id/$SMART_GROUP_ID" \
| xmllint --xpath '//computers/computer/id/text()' - 2>/dev/null)
# --- 3. assign each member to the target site ------------------------------
for id in $ids; do
echo "Assigning computer $id → site '$TARGET_SITE'"
curl -fsS -X PUT "${auth[@]}" -H "Content-Type: application/xml" \
"$JAMF_URL/JSSResource/computers/id/$id" \
-d "<computer><general><site><name>${TARGET_SITE}</name></site></general></computer>" \
>/dev/null
done
echo "Done — $(echo "$ids" | wc -w | tr -d ' ') computers reconciled."
What’s going on:
- Modern auth. Basic auth is gone on recent Jamf Pro, so the routine gets an OAuth bearer token from an API Role/Client at
/api/oauth/token. - Bearer tokens work on the Classic API. Membership and site assignment are still cleanest on the Classic endpoints, and the modern token authorizes them fine — so you get one auth model across both.
GET /JSSResource/computergroups/id/<id>returns the group’s members; thexmllint --xpathpulls out just the computer IDs.PUT /JSSResource/computers/id/<id>with a minimal<computer><general><site>body sets the site. You only send the field you’re changing.
The API role privileges
Keep the client least-privilege. It needs exactly:
- Read on Smart Computer Groups (to list members)
- Update on Computers (to set the site)
Nothing else.
The same thing as a Claude Code routine
If you’ve already stood up a Jamf MCP server, you don’t need the bash at all. Schedule a Claude Code routine that runs every few hours and tells the MCP, in plain language: “find the members of the ‘Entra – Marketing’ smart group and set each one’s site to Marketing.” Same loop, same API calls underneath — but the routine is a sentence, and you get a readable run log instead of cron output. It’s the natural follow-on to wiring an assistant to Jamf in the first place.
Caveats worth knowing
- A computer lives in exactly one site. Assigning a new site replaces the old one — make sure your groups don’t overlap, or decide which wins.
- This is one-way. When someone leaves the Entra group the Mac drops out of the smart group, but nothing moves it back out of the site. If you need that, add reconciliation: compare current site members against group members and reset the strays to your default site.
- It’s idempotent. Re-running re-asserts the same assignment, so a frequent schedule is safe — the PUTs are no-ops when nothing changed.
Takeaway
Site assignment can’t be driven by a smart group directly, but it’s a short hop with the API: read the Entra-tied group, PUT the site onto each member, run it on a schedule. Whether that schedule is cron or a Claude routine, the Macs sort themselves into the right site as people move around the org — and the regional admins only ever see what’s theirs.
Subscribe to gen/os
New write-ups on Apple device management — Jamf, Intune, Mosyle, scripting, and automation. Straight to your inbox, no spam, unsubscribe anytime.
Found this useful? Subscribe via RSS for new posts, orget in touch if I got something wrong.
Comments