Skip to content

Commit 1d66bca

Browse files
committed
New Script
1 parent a5516af commit 1d66bca

2 files changed

Lines changed: 818 additions & 0 deletions

File tree

Lines changed: 329 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,329 @@
1+
# Jamf Extension Attribute Usage Report 📊🧩
2+
3+
A command-line utility to rank **Jamf Pro Computer Extension Attributes** by how often they are referenced in **Smart Computer Groups**.
4+
5+
This script is **read-only**. It authenticates to Jamf Pro, reads extension attributes and computer groups, correlates Smart Group criteria back to extension attribute names, and produces a ranked report in the terminal with optional **JSON** and **HTML** output.
6+
7+
*Script file: `jamf-extension-attribute-usage-report.py`*
8+
9+
---
10+
11+
## What this script does
12+
13+
This report is built for a very specific operational question: which extension attributes are actually wired into live Smart Group logic, and how widely are they being used?
14+
15+
The script:
16+
17+
- Reads **Computer Extension Attributes** from Jamf Pro
18+
- Reads **Computer Groups** and filters down to **Smart Computer Groups**
19+
- Inspects Smart Group criteria
20+
- Matches criteria names back to extension attribute names
21+
- Ranks extension attributes by:
22+
- **Smart Group Count**: how many unique Smart Groups reference the attribute
23+
- **Criteria Hits**: how many total criteria references were found
24+
- Writes:
25+
- terminal table output
26+
- optional **JSON** output
27+
- optional **HTML** report with a visual ranking bar chart
28+
29+
This script does **not** clean up, archive, rename, or modify anything in Jamf Pro.
30+
31+
---
32+
33+
## Requirements
34+
35+
- **Jamf Pro** with API access
36+
- **Python 3.9+**
37+
- **requests** library
38+
- Network access to your Jamf Pro tenant
39+
40+
Install dependencies if needed:
41+
42+
~~~bash
43+
pip3 install requests
44+
~~~
45+
46+
---
47+
48+
## Authentication
49+
50+
The script supports two authentication paths:
51+
52+
1. **OAuth Client Credentials** (preferred)
53+
2. **Username / Password** bearer token fallback
54+
55+
### OAuth Client Credentials
56+
57+
~~~bash
58+
export JAMF_URL="https://yourorg.jamfcloud.com"
59+
export JAMF_CLIENT_ID="your_client_id"
60+
export JAMF_CLIENT_SECRET="your_client_secret"
61+
~~~
62+
63+
### Username / Password Fallback
64+
65+
~~~bash
66+
export JAMF_URL="https://yourorg.jamfcloud.com"
67+
export JAMF_USER="api_reader"
68+
export JAMF_PASSWORD="your_password"
69+
~~~
70+
71+
The script first tries client credentials against `/api/oauth/token`. If that is not available or fails, it can fall back to `/api/v1/auth/token` with username and password.
72+
73+
---
74+
75+
## Required Jamf API Role Privileges
76+
77+
For **client credentials**, the API role assigned to the client needs these minimum **read** privileges:
78+
79+
1. `Read Computer Extension Attributes`
80+
2. `Read Smart Computer Groups`
81+
3. `Read Static Computer Groups`
82+
83+
That third privilege is easy to miss. Even though the report only ranks **Smart Computer Groups**, the script first reads the broader computer-groups collection and then filters down to smart groups in code.
84+
85+
This script does **not** require any write privileges.
86+
87+
---
88+
89+
## API Role and Client Setup
90+
91+
The **API role** and **API client** are separate objects in Jamf Pro.
92+
93+
### Step 1: Create the API Role
94+
95+
Create a dedicated read-only role in Jamf Pro and grant:
96+
97+
- `Read Computer Extension Attributes`
98+
- `Read Smart Computer Groups`
99+
- `Read Static Computer Groups`
100+
101+
Suggested role name:
102+
103+
`ea-usage-report-readonly`
104+
105+
### Step 2: Create the API Client
106+
107+
Create a separate API client and assign the role above to it.
108+
109+
Suggested client name:
110+
111+
`ea-usage-report-client`
112+
113+
When the client is created, Jamf will give you:
114+
115+
- **Client ID**
116+
- **Client Secret**
117+
118+
Copy both immediately and store them securely.
119+
120+
> Important: if you change the role assignment later, rotate the client secret after the change. Otherwise the new privileges may not take effect for that client.
121+
122+
---
123+
124+
## Quick Start
125+
126+
Run the script with client credentials:
127+
128+
~~~bash
129+
export JAMF_URL="https://yourorg.jamfcloud.com"
130+
export JAMF_CLIENT_ID="your_client_id"
131+
export JAMF_CLIENT_SECRET="your_client_secret"
132+
133+
python3 jamf-extension-attribute-usage-report.py \
134+
--top 15 \
135+
--json-out ea-usage.json \
136+
--html-out ea-usage.html
137+
~~~
138+
139+
Run with username/password fallback:
140+
141+
~~~bash
142+
export JAMF_URL="https://yourorg.jamfcloud.com"
143+
export JAMF_USER="api_reader"
144+
export JAMF_PASSWORD="your_password"
145+
146+
python3 jamf-extension-attribute-usage-report.py
147+
~~~
148+
149+
Run the built-in parser/report self-test without touching Jamf:
150+
151+
~~~bash
152+
python3 jamf-extension-attribute-usage-report.py --self-test
153+
~~~
154+
155+
---
156+
157+
## Environment Variables
158+
159+
You can provide authentication via environment variables instead of passing values on the command line:
160+
161+
~~~bash
162+
export JAMF_URL="https://yourorg.jamfcloud.com"
163+
export JAMF_CLIENT_ID="your_client_id"
164+
export JAMF_CLIENT_SECRET="your_client_secret"
165+
~~~
166+
167+
or:
168+
169+
~~~bash
170+
export JAMF_URL="https://yourorg.jamfcloud.com"
171+
export JAMF_USER="api_reader"
172+
export JAMF_PASSWORD="your_password"
173+
~~~
174+
175+
---
176+
177+
## Output
178+
179+
### Terminal Table
180+
181+
By default the script prints a ranked terminal table like this:
182+
183+
~~~text
184+
Rank EA Name Smart Groups Criteria Hits Coverage
185+
---- ------------------------------ ------------ ------------- --------
186+
1 US CMMC 2.0 Level 2 (Enforce) 8 8 6.25%
187+
2 Compliance - Version 5 5 3.91%
188+
3 Adobe Updates 3 3 2.34%
189+
4 Default Browser 2 2 1.56%
190+
5 CMMC - pwpolicy 35-Day Inactiv 2 2 1.56%
191+
6 CMMC - FMSecure 2 2 1.56%
192+
7 Automated Enrollment Workflow 2 2 1.56%
193+
8 Account Status 2 2 1.56%
194+
9 Requres PW Change 1 1 0.78%
195+
10 Mac App Store Apps 1 1 0.78%
196+
11 Lockout Window 1 1 0.78%
197+
12 Jamf Trust VPN Status 1 1 0.78%
198+
13 Jamf Protect Version 1 1 0.78%
199+
14 Jamf Protect - Last Check-in 1 1 0.78%
200+
15 Jamf Connect - FirstRunDone 1 1 0.78%
201+
202+
Total extension attributes: 53
203+
Total smart groups scanned: 128
204+
Wrote JSON report to ea-usage.json
205+
Wrote HTML report to ea-usage.html
206+
~~~
207+
208+
### JSON Output
209+
210+
If `--json-out` is used, the script writes structured output with:
211+
212+
- generation timestamp
213+
- total extension attributes
214+
- total smart groups
215+
- ranked rows with counts, percentages, and Smart Group names
216+
217+
### HTML Output
218+
219+
If `--html-out` is used, the script writes a self-contained HTML report with:
220+
221+
- ranked extension attribute list
222+
- Smart Group counts
223+
- criteria hit counts
224+
- percentage coverage
225+
- horizontal visual bars for quick scanning
226+
227+
---
228+
229+
## CLI Options
230+
231+
~~~text
232+
--url <url> Jamf Pro base URL
233+
--client-id <id> OAuth client ID
234+
--client-secret <secret> OAuth client secret
235+
--user <user> Username fallback
236+
--password <password> Password fallback
237+
--json-out <file> Write JSON report to file
238+
--html-out <file> Write HTML report to file
239+
--top <n> Limit terminal output to top N rows
240+
--timeout <sec> HTTP timeout (default: 30)
241+
--insecure Disable TLS verification
242+
--self-test Run parser/report self-test only
243+
~~~
244+
245+
---
246+
247+
## How it works (in brief)
248+
249+
- Authenticates to Jamf Pro with either client credentials or user/pass bearer-token auth
250+
- Reads **Computer Extension Attributes** from the Classic API
251+
- Reads **Computer Groups** from the Classic API
252+
- Filters down to **Smart Computer Groups**
253+
- Walks each Smart Group’s criteria and matches criterion names against extension attribute names
254+
- Calculates:
255+
- number of unique Smart Groups per extension attribute
256+
- total number of criteria hits per extension attribute
257+
- percentage coverage across the Smart Group population
258+
- Sorts the results by Smart Group breadth first, then criteria hits, then name
259+
260+
---
261+
262+
## Safety Model
263+
264+
This script is **read-only**.
265+
266+
It does not:
267+
268+
- update extension attributes
269+
- modify Smart Groups
270+
- archive objects
271+
- rename anything
272+
- delete anything
273+
274+
It is intended as a visibility and reporting tool, not a cleanup engine.
275+
276+
---
277+
278+
## Troubleshooting
279+
280+
- **Client credentials fail but username/password works**
281+
The API role is likely missing one of the required read privileges, or the client secret was not rotated after a role change.
282+
283+
- **401 / invalid_client**
284+
Verify the client ID, client secret, API role assignment, and secret rotation status.
285+
286+
- **SSL errors**
287+
Fix trust for your Jamf environment, or use `--insecure` temporarily in lab/testing only.
288+
289+
- **Unexpected zero counts**
290+
Check for extension attribute names that collide with built-in inventory field names. Matching is name-based.
291+
292+
- **Timeouts on large tenants**
293+
Increase `--timeout` and rerun.
294+
295+
---
296+
297+
## Example Use Cases
298+
299+
- Find which extension attributes are most deeply tied into Smart Group scoping
300+
- Review extension-attribute dependency before renaming or rewriting an EA
301+
- Capture a point-in-time JSON or HTML artifact for change review
302+
- Spot extension attributes that appear in no Smart Group logic at all
303+
304+
---
305+
306+
## Roadmap
307+
308+
Potential future enhancements:
309+
310+
- support for Advanced Computer Search correlation
311+
- CSV export
312+
- more precise handling for name collisions with built-in inventory fields
313+
- optional filtering by category or prefix
314+
- unit tests / GitHub Actions example
315+
316+
---
317+
318+
## License
319+
320+
MIT License © 2026 Jon Brown
321+
322+
---
323+
324+
## Author
325+
326+
**Jon Brown**
327+
macOS | Jamf | DevSecOps | Automation
328+
https://jonbrown.org
329+
https://linkedin.com/in/jonbrown2

0 commit comments

Comments
 (0)