|
| 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