During the CMMC assessment, the Mac side of the environment needed more than a statement that controls were implemented. I needed to show how the macOS baseline connected to managed Macs, what Jamf was reporting, and how we could preserve enough evidence for the assessor without burying everyone in raw exports.
Jamf was central to that conversation because it connected several things that otherwise become hard to explain quickly: the macOS Security Compliance Project baseline, Jamf Compliance, scoped devices, smart groups, benchmark state, and live inventory. For the Mac fleet, Jamf gave me the management-plane view I wanted in front of the assessor. I could walk from the baseline to the managed devices and show how the environment was reporting instead of relying on a disconnected folder of screenshots.
That live view was important during the assessment, but I also needed evidence I could preserve after the conversation. A dashboard is useful while everyone is looking at it. An evidence package needs artifacts that can be opened later, compared against the baseline, and tied back to the assessment record. For Mac compliance, I wanted the Jamf view, the tailored baseline documentation, and a small summary artifact that showed the shape of the failed results without turning the first review into a device-by-device excavation.
Jamf As The Evidence System
The macOS Security Compliance Project gave me the baseline language and the control mapping. Jamf Compliance gave me a way to apply that baseline to the Mac fleet and see how scoped devices were reporting. That combination was the foundation for the Mac evidence story because it connected the written expectation to the managed endpoint population.
The tailored baseline PDF belongs in the evidence package because it explains what was configured and how the benchmark was shaped. Jamf belongs in the evidence conversation because it shows the current reporting state from the platform managing those Macs. Together, they let me talk through the Mac side of CMMC from the management plane rather than asking the assessor to infer everything from static files.
Jamf also provides a native reporting path for compliance benchmark evidence. Jamf documents a workflow using Advanced Computer Search: open Computers > Search Inventory, create a new Advanced Computer Search, use criteria tied to the compliance benchmark or related computer group, choose the fields for the report, and use the Reports tab to download or schedule it. Jamf: Build reports for Jamf Pro compliance benchmarks
I would keep that native Jamf report with the assessment evidence. It is the broad Jamf-generated artifact for the benchmark and device population, and it pairs naturally with the live dashboard review.
Why I Added A Summary Script
The reporting layer I still wanted was smaller and more focused. After showing the benchmark and the live Jamf view, I wanted a quick way to answer how many devices were reporting each failed compliance item. I did not need every inventory field to answer that. I needed a summary that could sit in the evidence package and give the assessor enough information to understand the fleet-level picture.
That is what JAMF Compliance Reports.py produces. The script is read-only, reads Jamf Pro computer inventory, extracts the failed-result Extension Attribute, and writes two CSV files:
compliance_failed_counts.csv
compliance_failed_by_device.csv
The counts file is the high-level artifact:
Item,Count
It shows each failed compliance item and how many devices are reporting it. That is the file I would start with when I want a concise evidence artifact. It gives the assessor a clear summary without handing over too much detail in the first pass.
The by-device file is the drill-down artifact:
ComputerName,Username,FailedItems
If the assessor wants to dig into a number from the summary, I can go to the by-device CSV, the native Jamf report, or the live Jamf dashboard. The summary gives enough context to start the review, and the other artifacts keep the path back to source data intact.
The script lives here: macOS-JAMF-Scripts: Scripts/Security/NIST.
The Failed-Result Extension Attribute
The script default is:
EA_DEFAULT_NAME = "Compliance - Failed Result List"
That generic Extension Attribute still exists in my local Jamf evidence. In the EA usage report I generated on June 17, 2026, Compliance - Failed Result List appears as an Extension Attribute.
For my active CMMC benchmark evidence, the more relevant Extension Attribute is benchmark-specific:
US CMMC 2.0 Level 2 (Enforce) - Failed Result List
That benchmark-specific EA appears in my smart group usage report for the active CMMC groups, including the compliant and non-compliant groups for the baseline and macOS versions. The failed-result EA pattern is still part of the Jamf Compliance and macOS Security Compliance Project workflow, while the exact EA name depends on the benchmark name and how the compliance content was generated.
The compatibility point is --ea-name. For my CMMC Level 2 evidence export, I would run the script against the benchmark-specific EA:
export JAMF_URL="https://yourorg.jamfcloud.com"
export JAMF_CLIENT_ID="your_client_id"
export JAMF_CLIENT_SECRET="your_client_secret"
/usr/local/bin/managed_python3 "JAMF Compliance Reports.py" \
--ea-name "US CMMC 2.0 Level 2 (Enforce) - Failed Result List" \
--out-dir "./Reports"
Before running the script in another tenant, I would confirm the failed-result EA name in Jamf and pass the exact value. If the generic EA is the right source, the default works. If the active benchmark has its own failed-result EA, I would use the benchmark-specific name.
Keeping The Export Read-Only
For audit evidence collection, I want the reporting tool to read Jamf and write local CSV files. I do not want it changing the Jamf environment.
The preferred authentication path is OAuth client credentials:
export JAMF_URL="https://yourorg.jamfcloud.com"
export JAMF_CLIENT_ID="your_client_id"
export JAMF_CLIENT_SECRET="your_client_secret"
The script also supports username and password as a fallback:
export JAMF_URL="https://yourorg.jamfcloud.com"
export JAMF_USER="jamf_api_reader"
export JAMF_PASSWORD="••••••••"
For this report, the API role should be limited to inventory read access and the authentication endpoints. The script authenticates, reads computer inventory, extracts the failed-result EA, normalizes the values, and writes the CSV outputs.
The inventory request asks for the sections required for the report:
params = {
"section": "EXTENSION_ATTRIBUTES,GENERAL,USER_AND_LOCATION",
"page": page,
"page-size": page_size,
"sort": "id:asc",
}
r = client.get("/api/v1/computers-inventory", params=params)
GENERAL provides the computer name, USER_AND_LOCATION provides the user context, and EXTENSION_ATTRIBUTES provides the failed-result EA. Those fields are enough for the summary and the drill-down CSV.
The parser handles JSON, lists, comma-separated values, semicolon-separated values, pipe-delimited values, and multiline strings. It also handles No baseline set as a special value: the phrase remains visible on the per-device report, while the fleet counts exclude it so the summary is not distorted by scoping noise.
Reviewing The Output
After the script runs, I usually look at the counts first:
(head -n 1 Reports/compliance_failed_counts.csv && \
tail -n +2 Reports/compliance_failed_counts.csv | sort -t, -k2,2nr | head -20) | column -s, -t
Then I spot-check the device file:
(head -n 1 Reports/compliance_failed_by_device.csv && \
tail -n +2 Reports/compliance_failed_by_device.csv | grep -v ',$' | head -15) | column -s, -t
If the CSVs are empty or do not match what I expect from the dashboard, I check the EA name, the API role, and the benchmark scope first. In this workflow, scope is usually the fastest way to create confusing evidence. The report can only summarize what Jamf is actually returning for the devices assigned to the benchmark.
The Evidence Package I Would Keep
For the Mac side of the CMMC assessment, I would keep the evidence package organized around the way the assessor conversation actually works:
- Tailored baseline PDF from the macOS Security Compliance Project and Jamf Compliance workflow.
- Native Jamf compliance benchmark report or dashboard export.
compliance_failed_counts.csvas the high-level failed-item summary.compliance_failed_by_device.csvas the drill-down artifact.- Live Jamf dashboard review during the assessment.
That package lets me start with the baseline, move into Jamf as the management system, preserve the broad native Jamf report, and then use the script output as a concise summary of failed items across the fleet. If the assessor wants detail, the evidence trail can move from the count to the device list and then back into Jamf.
Jamf was key because it gave the Mac evidence a real management-plane source. The script helped me preserve one focused slice of that data in a format that was easier to review and easier to keep with the assessment record.
Sources
- Jon Brown: macOS-JAMF-Scripts NIST folder
- Jamf: Build reports for Jamf Pro compliance benchmarks
- Jamf: Establishing Compliance Baselines with Compliance Editor
- macOS Security Compliance Project
- Apple Platform Certifications: macOS Security Compliance Project
- Cybersecurity Maturity Model Certification
- NIST SP 800-171 Revision 2
AI Usage Transparency Report
AI Era · Written during widespread use of AI tools
AI Signal Composition
Score: 0.31 · Moderate AI Influence
Summary
The script generates two CSV files: compliance_failed_counts.csv and compliance_failed_by_device.csv. The counts file shows each failed compliance item and how many devices are reporting it, while the by-device file provides a drill-down artifact for further review.
Related Posts
How We Structured and Hashed CMMC Evidence for Auditor Review
How folder naming, control-level artifact names, spreadsheet hyperlinks, and evidence hashing made a CMMC evidence package easier for the auditor to validate.
Opening the Ollama Black Box: Understanding the Trust Boundary Behind Local AI
Installing Ollama is easy. Understanding the trust boundary behind a local AI service is what determines whether it belongs in an automation workflow.
Your Vibe-Coded App Still Needs a Trustworthy Release Path
Why vibe-coded apps still need release discipline: code signing, notarization, checksums, and GitHub artifact attestations all support integrity and user trust.
Secure Storage Isn't Enough: Using Secrets Safely in Admin Automation
Secret managers protect stored credentials. They don't automatically protect how your automation uses them. Here's the review process I use before workflows reach production.
How I Keep Up With ISC2 CPE Credits Without Making It a Second Job
Keeping up with ISC2 CPE credits is easier when you treat it like a normal professional habit instead of a renewal emergency. Here is the system I use across CISSP, CCSP, SSCP, and CSSLP, with free and low-friction sources for webinars, books, training, and work-based credits.
When AI Agents Trust the Wrong Tool Description
Microsoft's MCP tool-poisoning research shows why AI agent security has to treat tool descriptions, schemas, and metadata as part of the control plane instead of harmless documentation.
Updating Jamf Pro Compliance Baselines from the macOS Security Compliance Project
How to update an existing Jamf Pro Compliance benchmark when new macOS Security Compliance Project baseline content becomes available.
The CMMC Evidence Collection Guide I Wish I Had Before My Assessment
When I started preparing for a CMMC assessment, I expected to spend most of my time focused on policies, procedures, and the System Security Plan. Those things are certainly important, but what surprised me was how much of the assessment ultimately came down to evidence.
How We Passed Our CMMC Assessment
After helping lead our organization through a successful CMMC Level 2 assessment, I share lessons learned from years of preparation, audit readiness, evidence collection, and working through the certification process.
Setting up Ollama on macOS
Recently, after some bad experiences with OpenAI's ChatGPT and CODEX, I decided to look into and learn more about running local AI models. On its face it was intimidating, but I had seen a lot of people in the MacAdmins community posting examples of macOS setups, which really helped lower the bar for me both in terms of approachability and just making me more aware of the local AI community that exists out there today.