Skip to content

Commit eb0e352

Browse files
committed
Add Jamf API role audit script
1 parent 1d66bca commit eb0e352

2 files changed

Lines changed: 630 additions & 0 deletions

File tree

Lines changed: 218 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,218 @@
1+
# Jamf API Role Audit
2+
3+
Audit Jamf Pro API roles and API clients without changing anything in Jamf.
4+
5+
This tool is designed for security review, compliance evidence, and access hygiene. It answers the practical question: **which Jamf API roles exist, what privileges do they contain, and which roles deserve review first?**
6+
7+
> Script: `jamf_api_role_audit.py`
8+
9+
---
10+
11+
## What It Does
12+
13+
- Authenticates to Jamf Pro using OAuth client credentials.
14+
- Reads API roles from `/api/v1/api-roles`.
15+
- Reads API integrations from `/api/v1/api-integrations`.
16+
- Expands role details where Jamf exposes them.
17+
- Counts total privileges per API role.
18+
- Flags write-like privileges such as create, update, delete, write, flush, and send.
19+
- Assigns a review priority to each role.
20+
- Writes JSON and CSV reports for audit review.
21+
22+
This script is report-only. It does not revoke, delete, rotate, disable, or modify API clients or API roles.
23+
24+
---
25+
26+
## Required Jamf API Role
27+
28+
Create a dedicated audit role in Jamf Pro with the narrowest permissions needed to inspect API roles and integrations.
29+
30+
Recommended role name:
31+
32+
```text
33+
API Client Audit
34+
```
35+
36+
Required privileges:
37+
38+
```text
39+
Read API Integrations
40+
Read API Roles
41+
```
42+
43+
Then create an API client assigned to that role, for example:
44+
45+
```text
46+
API Client Auditor
47+
```
48+
49+
Store the client ID and client secret securely. Do not paste real secrets into tickets, documentation, blog posts, or shared notes.
50+
51+
---
52+
53+
## Quick Start
54+
55+
From the folder containing `jamf_api_role_audit.py`:
56+
57+
```bash
58+
export JAMF_URL="https://yourtenant.jamfcloud.com"
59+
export JAMF_CLIENT_ID="paste_client_id_here"
60+
export JAMF_CLIENT_SECRET="paste_client_secret_here"
61+
62+
PYTHONDONTWRITEBYTECODE=1 python3 ./jamf_api_role_audit.py \
63+
--json-out jamf-api-client-role-audit.json \
64+
--csv-out jamf-api-client-role-audit.csv \
65+
--roles-csv-out jamf-api-role-audit.csv
66+
```
67+
68+
Outputs:
69+
70+
- `jamf-api-role-audit.csv` — primary role audit report
71+
- `jamf-api-client-role-audit.csv` — API client inventory report
72+
- `jamf-api-client-role-audit.json` — full JSON output with roles, integrations, and raw role records
73+
74+
Open the role report first:
75+
76+
```bash
77+
open jamf-api-role-audit.csv
78+
```
79+
80+
---
81+
82+
## Example Output
83+
84+
```text
85+
API roles scanned: 7
86+
Roles with write-like privileges: 4
87+
API clients inventoried: 8
88+
Role usage count: unavailable from tested Jamf API responses. This output ranks role privilege reach, not client assignment count.
89+
90+
ROLE: Security reporting connector (6)
91+
privileges=30, write-like=18, priority=review write access
92+
- Create Computer Extension Attributes
93+
- Create iOS Configuration Profiles
94+
- Create macOS Configuration Profiles
95+
- Create Mobile Device Extension Attributes
96+
- Create Static Computer Groups
97+
- Delete Computer Extension Attributes
98+
- Delete Mobile Device Extension Attributes
99+
- Read Computer Extension Attributes
100+
- Read Computers
101+
- Read iOS Configuration Profiles
102+
- Read Mac Applications
103+
- Read macOS Configuration Profiles
104+
- Read Mobile Device Applications
105+
- Read Mobile Device Extension Attributes
106+
- Read Mobile Devices
107+
- Read Smart Computer Groups
108+
- Read Smart Mobile Device Groups
109+
- Read Static Computer Groups
110+
- Read Static Mobile Device Groups
111+
- Update Computer Extension Attributes
112+
- Update Computers
113+
- Update iOS Configuration Profiles
114+
- Update macOS Configuration Profiles
115+
- Update Mobile Device Extension Attributes
116+
- Update Mobile Devices
117+
- Update Smart Computer Groups
118+
- Update Smart Mobile Device Groups
119+
- Update Static Computer Groups
120+
- Update Static Mobile Device Groups
121+
- Update User
122+
123+
ROLE: Patch workflow (3)
124+
privileges=8, write-like=4, priority=review write access
125+
- Create Categories
126+
- Create macOS Configuration Profiles
127+
- Create Policies
128+
- Create Scripts
129+
- Read Categories
130+
- Read macOS Configuration Profiles
131+
- Read Policies
132+
- Read Scripts
133+
```
134+
135+
---
136+
137+
## How Priority Is Assigned
138+
139+
Priority is based on role privileges:
140+
141+
| Priority | Rule | Meaning |
142+
|---|---|---|
143+
| `review write access` | One or more write-like privileges | Start here. The role can likely change Jamf state. |
144+
| `review broad read access` | Ten or more privileges and no write-like privileges | Review for sensitive read reach. |
145+
| `lower priority` | Fewer than ten privileges and no write-like privileges | Keep in inventory, but review after broader or write-capable roles. |
146+
147+
The write-like check looks for privilege names containing:
148+
149+
```text
150+
create, update, delete, write, flush, send
151+
```
152+
153+
This is a triage label, not an automatic risk score. A low-priority role may still matter if it reads sensitive data.
154+
155+
---
156+
157+
## Report Columns
158+
159+
### `jamf-api-role-audit.csv`
160+
161+
| Column | Meaning |
162+
|---|---|
163+
| `id` | Jamf API role ID |
164+
| `name` | Role display name |
165+
| `created` | Created timestamp when Jamf exposes it |
166+
| `updated` | Updated timestamp when Jamf exposes it |
167+
| `privilege_count` | Total privilege count |
168+
| `write_privilege_count` | Count of write-like privileges |
169+
| `write_privileges` | Semicolon-separated write-like privileges |
170+
| `privileges` | Full semicolon-separated privilege list |
171+
| `linked_integration_count` | Count of linked API clients when Jamf exposes the mapping |
172+
| `linked_integrations` | Linked API client names when Jamf exposes the mapping |
173+
| `link_status` | Mapping status |
174+
| `review_priority` | Triage label for review order |
175+
176+
### `jamf-api-client-role-audit.csv`
177+
178+
| Column | Meaning |
179+
|---|---|
180+
| `id` | Jamf API integration/client ID |
181+
| `name` | API integration/client display name |
182+
| `enabled` | Enabled state when Jamf exposes it |
183+
| `created` | Created timestamp when Jamf exposes it |
184+
| `updated` | Updated timestamp when Jamf exposes it |
185+
| `last_used` | Last-used timestamp when Jamf exposes it |
186+
| `role_mapping_status` | Whether Jamf exposed role mapping in the tested response |
187+
| `role_names` | Mapped role names when available |
188+
| `privilege_count` | Privilege count when role mapping is available |
189+
| `write_privilege_count` | Write-like privilege count when role mapping is available |
190+
| `write_privileges` | Write-like privileges when role mapping is available |
191+
192+
---
193+
194+
## Important Boundary
195+
196+
In tested Jamf Pro responses, API roles and API integrations may be returned separately without exposing which API client is assigned to which role. When that happens, the script does **not** guess.
197+
198+
The terminal output focuses on role privilege reach because that is the reliable audit signal. The API client inventory is still written to CSV, but client-to-role usage is only reported when Jamf exposes that mapping through the API response.
199+
200+
---
201+
202+
## Security Notes
203+
204+
- Report-only: no delete, revoke, disable, rotate, or update actions.
205+
- Uses OAuth client credentials.
206+
- Does not print client secrets.
207+
- Does not recover existing client secrets; Jamf should not expose those after creation.
208+
- Intended for periodic access review and audit evidence.
209+
210+
---
211+
212+
## Requirements
213+
214+
- Python 3.10+
215+
- Jamf Pro API access
216+
- API client with `Read API Integrations` and `Read API Roles`
217+
218+
The script uses Python standard library modules only.

0 commit comments

Comments
 (0)