Repository navigation
Hands On With Jamf Concepts
A guided walk through jamf-cli, run against a shared sandbox, then a buffet to pick from. No prior jamf-cli experience needed.
The machine in front of you already holds jamf-cli and a saved sandbox profile. We checked every command below against that sandbox on v1.31.1, and the bulk-operation and profile-conversion commands again on v1.33.0. Each snippet is one command — use the copy button and paste into Terminal.
Already know the CLI? Skip to Buffet and pick what interests you. Ask Keaton or Neil anything.
Self Service holds the username and password for the sandbox. Open it before anything else:
open "jamfselfservice://content?entity=policy&id=20&action=view"Keep that window handy. You need those credentials when a command sends you to the Jamf Pro web interface. The CLI itself is already signed in on this Mac.
Which products does this CLI cover?
jamf-cliOne screen for the whole instance — version, fleet counts, certificate expiry, feature flags, object counts:
jamf-cli pro overviewjamf-cli pro --help lists every Jamf Pro command. The sandbox credential covers Jamf Pro, blueprints and compliance benchmarks.
You do not have to memorise any of it. Press Tab while you type and this Mac completes command names and flags for you. Try it — type jamf-cli pro comp and press Tab to see every command that starts that way.
jamf-cli dashboard --out-file ~/Desktop/fleet.html && open ~/Desktop/fleet.htmlIt takes about three seconds. Self-contained HTML, light and dark themes, no external assets, nothing to host. Fleet counts, security posture, OS spread, audit findings and blueprint state, from one command.
It prints five Jamf Security Cloud errors to stderr and omits that section, because this credential does not reach Security Cloud.
jamf-cli pro computers listA table, although you did not ask for one. The format follows the destination: a terminal gets a table, a pipe or a file gets JSON. Name the format with -o when you need a particular one.
One field, one value per line. Pipe this into anything:
jamf-cli pro computers list --field serialA spreadsheet:
jamf-cli pro computers list -o csv --out-file ~/Desktop/fleet.csv--field and --select name fields in whichever shape your format prints. In a table those names are the column labels: id, name, serial, model, osVersion, lastCheckIn. -o json prints the record's dot paths instead, like hardware.serialNumber. Add -o json and the dot paths work in a terminal and through a pipe alike.
JNUC Lab - Playground is a disabled policy that exists for you to edit. Look at its scope:
jamf-cli pro classic-policies scope get --name "JNUC Lab - Playground" -o tableAdd a smart group to its targets, by name:
jamf-cli pro classic-policies scope add --name "JNUC Lab - Playground" --computer-group "JNUC Lab - macOS 26" -o tableThe command sends only the <scope> element, so nothing else about the policy changes. Remove it again:
jamf-cli pro classic-policies scope remove --name "JNUC Lab - Playground" --computer-group "JNUC Lab - macOS 26" -o tableOther people in the room may edit the same policy. scope get always shows the live scope.
Through a pipe the output is JSON, which is the only reason jq can read it:
jamf-cli pro computers list | jq lengthOS version breakdown, most common first:
jamf-cli pro computers list --section OPERATING_SYSTEM -o json | jq -r 'group_by(.operatingSystem.version) | map({v: .[0].operatingSystem.version, n: length}) | sort_by(-.n)[] | "\(.n)\t\(.v)"'That is the five minutes. Everything below is optional — pick what interests you, or ask Keaton or Neil.
Pick anything. We checked all of them against this sandbox.
Some of these create objects. Set your initials first so your objects stay separate from your neighbour's:
export ME=<your initials>Name your own fields. --select takes dot paths under -o json:
jamf-cli pro computers list --select general.name,hardware.model,operatingSystem.version -o jsonOr the table's own column labels:
jamf-cli pro computers list --select name,serial,osVersionAn RSQL filter, run server-side:
jamf-cli pro computers list --filter 'operatingSystem.version=="26.6.0"'Sort and cap:
jamf-cli pro computers list --sort general.lastContact:desc --limit 5Find one record without knowing its ID:
jamf-cli pro computers get --serial FWWT058JG5 --select general.name,hardware.model,operatingSystem.version,operatingSystem.fileVault2Status -o jsonSome commands have no summary row and return the API's own wide rows. --compact drops the bulky columns:
jamf-cli pro compliance-benchmarks listjamf-cli pro compliance-benchmarks list --compactSee the HTTP calls. The first goes to /pro/v1/categories, the second to /proclassic/policies — one credential, two APIs, both through the Platform gateway:
jamf-cli pro categories list -vjamf-cli pro classic-policies list -vCross-resource health checks, ranked by severity. It reads many endpoints and takes about 15 seconds:
jamf-cli pro auditFileVault, firewall, Gatekeeper and SIP percentages:
jamf-cli pro report security -o tablePolicy health, including scope problems:
jamf-cli pro report policy-status -o tableThere are 15 reports in all:
jamf-cli pro report --helpWhat can be backed up:
jamf-cli pro backup list-resources -o plainExport every policy as its own YAML file:
jamf-cli pro backup --output ~/Desktop/backup-before --resources policiesChange one policy's scope:
jamf-cli pro classic-policies scope add --name "JNUC Lab - Playground" --computer-group "JNUC Lab - macOS 26" -o tableExport again:
jamf-cli pro backup --output ~/Desktop/backup-after --resources policiesCompare the two directories:
jamf-cli pro diff --source ~/Desktop/backup-before --target ~/Desktop/backup-after -o json | jq -r '.[] | "\(.resource)\t\(.name)\t\(.change)\t\(.field)"'Remove it from the scope again:
jamf-cli pro classic-policies scope remove --name "JNUC Lab - Playground" --computer-group "JNUC Lab - macOS 26" -o tablepro diff also compares two live instances. Pass profile names instead of directories.
What does the body look like?
jamf-cli pro categories create --scaffoldCreate one and keep its id:
CAT=$(echo "{\"name\":\"JNUC Lab $ME\",\"priority\":9}" | jamf-cli pro categories create --field id) && echo "created category $CAT"Read it back:
jamf-cli pro categories get $CAT -o tableRead it, change one field and write it back, in one line:
jamf-cli pro categories get $CAT -o json | jq '.priority = 1' | jamf-cli pro categories update $CATDelete it:
jamf-cli pro categories delete $CAT --yesClassic API resources take XML. --scaffold prints a complete template:
jamf-cli pro classic-policies create --scaffoldCreate a disabled policy of your own:
POLICY_ID=$(jamf-cli pro classic-policies create --field policy.id --quiet <<EOF
<policy>
<general>
<name>JNUC Lab - $ME</name>
<enabled>false</enabled>
<frequency>Ongoing</frequency>
</general>
</policy>
EOF
) && echo "created policy $POLICY_ID"Exclusions work the same way as targets:
jamf-cli pro classic-policies scope add $POLICY_ID --section exclusion --computer-group "JNUC Lab - Rotation" -o table--set builds a Classic body in dot notation, and -n previews any write without sending it:
jamf-cli pro classic-policies update $POLICY_ID --set general.name="JNUC Lab - $ME v2" -n--set refuses a value outside the enum. The Classic API accepts one and silently substitutes its default, so this refusal is the only warning you get:
jamf-cli pro classic-policies update $POLICY_ID --set general.frequency="Twice per fortnight" -nDelete the policy:
jamf-cli pro classic-policies delete $POLICY_ID --yesYou can run any command in a loop. Make ten policies:
for i in $(seq 1 10); do
jamf-cli pro classic-policies create --field policy.id --quiet <<EOF
<policy><general><name>JNUC Lab - $ME $i</name><enabled>false</enabled><frequency>Ongoing</frequency></general></policy>
EOF
doneCollect their IDs by name:
jamf-cli pro classic-policies list -o json | jq -r ".[] | select(.name | startswith(\"JNUC Lab - $ME \")) | .id"Scope all ten to one group in one pass. Each scope add writes the new scope to stdout and its confirmation to stderr, so >/dev/null leaves you one line per policy:
for id in $(jamf-cli pro classic-policies list -o json | jq -r ".[] | select(.name | startswith(\"JNUC Lab - $ME \")) | .id"); do
jamf-cli pro classic-policies scope add $id --computer-group "JNUC Lab - macOS 26" --quiet >/dev/null
doneTen scoped policies in about five seconds. One report command now finds what you just made. Each policy carries a scope and no enabled flag, and pro report policy-status names every one:
jamf-cli pro report policy-status -o tableDelete them again:
for id in $(jamf-cli pro classic-policies list -o json | jq -r ".[] | select(.name | startswith(\"JNUC Lab - $ME \")) | .id"); do
jamf-cli pro classic-policies delete $id --yes --quiet
doneEvery group, with its membership count and its Platform UUID:
jamf-cli pro groups list -o tableLargest groups first:
jamf-cli pro groups list -o json | jq -r '.[] | select(.groupType=="COMPUTER") | "\(.membershipCount)\t\(.groupName)"' | sort -rnA smart group's criteria and member count:
jamf-cli pro classic-computer-groups get 7101 -o json | jq '{name, criteria, members: (.computers | length)}'The body for a new smart group:
jamf-cli pro classic-computer-groups create --scaffoldA list, a group or a filter prints a preview first and changes nothing until you pass --yes.
Preview adding one group's members to the static group:
jamf-cli pro classic-computer-groups add-members --name "JNUC Lab - Rotation" --from-group "JNUC Lab - macOS 26"Now add them:
jamf-cli pro classic-computer-groups add-members --name "JNUC Lab - Rotation" --from-group "JNUC Lab - macOS 26" --yesRemove them again:
jamf-cli pro classic-computer-groups remove-members --name "JNUC Lab - Rotation" --from-group "JNUC Lab - macOS 26" --yesPreview an MDM command to 12 devices:
jamf-cli pro computer-inventory blank-push --group "JNUC Lab - macOS 26" --dry-runA destructive command needs two flags, not one:
jamf-cli pro computer-inventory erase --group "JNUC Lab - macOS 26" --yes --confirm-destructiveWhat blueprints exist:
jamf-cli pro blueprints list -o tableThe full definition:
jamf-cli pro blueprints get --name "JNUC Lab - Math Settings" -o jsonPortable JSON, ready to apply to another tenant:
jamf-cli pro blueprints export --name "JNUC Lab - Math Settings" -o json > ~/Desktop/bp.jsonThe 17 component types a blueprint can hold:
jamf-cli pro blueprints components list -o tableExample configuration for one component:
jamf-cli pro blueprints components scaffold com.jamf.ddm.math-settingsWhich device groups it targets:
jamf-cli pro blueprints scope list --name "JNUC Lab - Math Settings" -o tableClone it:
jamf-cli pro blueprints clone "JNUC Lab - Math Settings" "zz-$ME-clone"Delete the clone:
jamf-cli pro blueprints delete --name "zz-$ME-clone" --yesPick a profile:
jamf-cli pro classic-macos-config-profiles list -o tableConvert it, with your initials in the name:
jamf-cli pro blueprints import-profile 9650 --convert --blueprint-name "zz-$ME-ddm"The output names each payload, says which native DDM component it maps to, and warns about the keys that have no DDM equivalent.
Read the blueprint the conversion built. One Software Update payload became a native DDM component, and the rest stayed in a configuration-profile wrapper:
jamf-cli pro blueprints get --name "zz-$ME-ddm" -o json | jq '{name, state: .deploymentState.state, components: [.steps[].components[].identifier]}'Then delete it:
jamf-cli pro blueprints delete --name "zz-$ME-ddm" --yesThe 15 baselines Jamf ships:
jamf-cli pro baselines list --select baselineId,title,ruleCount -o jsonBenchmarks on this tenant:
jamf-cli pro compliance-benchmarks list --compact -o tablePer-rule pass and fail counts:
jamf-cli pro benchmark-reports rules --name "JNUC Lab - CIS Level 1 (Monitor)" -o json | jq -r '.[] | [.ruleNumber, .passed, .failed, .ruleTitle] | @tsv' | head -20Overall percentage:
jamf-cli pro benchmark-reports compliance-percentage --name "JNUC Lab - CIS Level 1 (Monitor)" -o tableA full 104-rule benchmark body, pre-populated from a baseline:
jamf-cli pro compliance-benchmarks apply --scaffold-from-baseline cis_lvl1 | jq '{title, sourceBaselineId, enforcementMode, rules: (.rules|length)}'Creating a benchmark builds Jamf Pro objects for you. Three smart groups:
jamf-cli pro classic-computer-groups list -o json | jq -r '.[] | select(.name | test("CIS Level 1")) | .name'One policy:
jamf-cli pro classic-policies list -o json | jq -r '.[] | select(.name | test("CIS Level 1")) | .name'One script:
jamf-cli pro scripts list -o json | jq -r '.[] | select(.name | test("compliance_benchmark")) | .name'The full catalog Jamf offers:
jamf-cli pro app-installers-titles list --select titleName,publisher,version -o json | jq -r '.[] | [.publisher, .titleName, .version] | @tsv' | head -20Deployments configured here:
jamf-cli pro app-installers-deployments list --select name,app.bundleId,app.deployedVersion,smartGroup.name -o jsonThese open a browser, which asks you to sign in. The credentials are in Self Service — see Start here.
Section names the CLI knows:
jamf-cli pro open --list -o json | jq -r '.[].name'Open one:
jamf-cli pro open smart-computer-groupsPrint the URL instead of opening it:
jamf-cli pro open blueprints --printOperating guidance for an agent — auth, exit codes, flag conventions:
jamf-cli agent-contextServe the whole command tree over MCP:
jamf-cli mcp --helpThe MCP server gives a connected model three tools: list_commands, run_command and generate_report. Credentials never cross the protocol — child commands inherit the profile the server started with.
| Object | Detail |
|---|---|
| 45 computers, 60 mobile devices, 192 users | simulated inventory |
JNUC Lab - MacBook Fleet |
smart group, 30 members, criterion Model like MacBook
|
JNUC Lab - macOS 26 |
smart group, 12 members, criterion Operating System Version like 26.
|
JNUC Lab - Rotation |
static group, empty: the target for add-members
|
JNUC Lab - Playground |
disabled policy, scoped to JNUC Lab - MacBook Fleet
|
JNUC Lab - Math Settings |
blueprint with one DDM math-settings component |
JNUC Lab - CIS Level 1 (Monitor) |
compliance benchmark, 104 rules, monitor mode |
Write freely. Anything named JNUC Lab - <your initials> is yours. We rebuild the fixtures above between sessions.
No command writes without your consent. -n previews any write. Mutating commands need --yes. Destructive MDM commands need --yes and --confirm-destructive. Bulk commands print a preview table and exit.
Exit codes are stable. 0 success, 2 usage, 4 not found, 8 refused by the gateway. Full table: Error Handling & Exit Codes.
- Setup Guide — install it and add your own profile
- Pro Workflows — more Jamf Pro recipes
- Platform Workflows — blueprints and benchmarks in depth
-
CLI Patterns —
apply,--scaffold,--set, pagination -
Output Formats — the full
-omatrix - MCP Server — connect Claude or another MCP client
- Claude Code Skills — packaged Jamf workflows for Claude Code
Repository · Issues · Releases
jamf-cli Wiki
- Home
- Community
- Getting Started
- CLI Reference
- Product Commands
- Workflows
- Configuration
- Reference
Products
- Jamf Pro:
jamf-cli pro - Jamf Platform API:
jamf-cli pro(blueprints, benchmarks, DDM reports) - Jamf Platform:
jamf-cli platform(AI Governance, Jamf Account, audit) - Jamf Protect:
jamf-cli protect - Jamf School:
jamf-cli school - Jamf Security Cloud:
jamf-cli security