Scripts
Shell scripts that audit compliance status and remediate non-compliant settings.
Deploy via MDM to continuously monitor and enforce your security baseline.
What Are Compliance Scripts?โ
Compliance scripts are shell scripts (.sh files) that run directly on a Mac. They do two things:
- Audit: Check if settings match your baseline requirements
- Remediate: Change settings to match requirements if they don't
Unlike configuration profiles (which declare settings for macOS to enforce), scripts actively execute commands. This makes them more flexible. They can check anything you can query via command line, and fix anything you can change via command line.
How the Compliance Script Worksโ
MACE can generate either a single combined compliance script or individual scripts per rule (configured in Build Options). The combined script contains functions for every enabled rule. Here's what happens when it runs:
Script Modesโ
The generated script supports three execution modes:
Runs all audit functions and reports pass/fail for each rule. Does NOT change any settings. Use this to assess current compliance status without making changes.
Runs all remediation functions to apply correct settings. Use this when you want to bring a device into compliance. Typically run after --check identifies failures.
Runs audit, then remediation, then audit again. This verifies that fixes were actually applied successfully. The final audit confirms the device is now compliant.
Example: What a Rule Looks Like in the Scriptโ
For a rule like "Enable Firewall Logging", the script contains:
# Rule: os_firewall_log_enable
# STIG ID: APPL-15-005001
# Severity: medium
# Discussion: Firewall logging MUST be enabled...
audit_os_firewall_log_enable() {
# Check command from the rule YAML
local result=$(/usr/libexec/ApplicationFirewall/socketfilterfw \
--getloggingmode 2>/dev/null | grep -c "Log mode is on")
# Expected result from the rule YAML
local expected="1"
if [[ "$result" == "$expected" ]]; then
logmessage "os_firewall_log_enable: PASSED"
return 0
else
logmessage "os_firewall_log_enable: FAILED (expected: $expected, got: $result)"
return 1
fi
}
fix_os_firewall_log_enable() {
# Fix command from the rule YAML
/usr/libexec/ApplicationFirewall/socketfilterfw --setloggingmode on
logmessage "os_firewall_log_enable: Fixed"
}
What's happening here:
- The
audit_function runs the check command defined in the rule's YAML - It compares the result against the expected value
- Returns pass (0) or fail (1) and logs the result
- The
fix_function runs the remediation command to correct the setting
Extension Attributesโ
Extension attributes (EAs) are individual scripts that report a single value back to your MDM. They're designed for MDM inventory collection.
How Extension Attributes Workโ
On a schedule (e.g., daily) or when you request a device update, your MDM tells the Mac to collect inventory.
The MDM agent executes every extension attribute script on the device.
Each script prints a value (like "Compliant" or "Non-Compliant") in a format the MDM expects.
The result appears in the device's inventory record. You can search, report, and create smart groups based on these values.
Example Extension Attributeโ
#!/bin/zsh
# Extension Attribute: Firewall Logging
# Checks if firewall logging is enabled
result=$(/usr/libexec/ApplicationFirewall/socketfilterfw \
--getloggingmode 2>/dev/null | grep -c "Log mode is on")
if [[ "$result" == "1" ]]; then
echo "<result>Compliant</result>"
else
echo "<result>Non-Compliant</result>"
fi
The output format matters: Different MDMs expect different formats:
- Jamf Pro:
<result>value</result> - Kandji: Plain text output
- Mosyle: Plain text output
- Intune: Specific schema requirements
MACE generates EAs in the correct format for each supported MDM.
Deploying Scripts via MDMโ
Jamf Proโ
๐ Deploying the Audit Script
In Jamf Pro, navigate to Settings โ Computer Management โ Scripts.
Click "New" and paste the contents of your compliance script. Set the script to run as the logged-in user or as root (most compliance checks need root).
Add a parameter for the mode: $4 can be --check, --fix, or --cfc.
Go to Computers โ Policies โ New. Add your script and set the parameter to --check for audit-only runs.
Choose when to run: Recurring Check-in (daily/weekly), Startup, or manual trigger. Scope to your target computers.
๐ Deploying Extension Attributes
In Jamf Pro, navigate to Settings โ Computer Management โ Extension Attributes.
Click "New" and set Data Type to "String". Set Input Type to "Script".
Copy the contents of the extension attribute script (e.g., ea_os_firewall_log_enable.sh) into the script field.
The EA runs during the next inventory collection. You can force this by running sudo jamf recon on a test Mac.
Create a Smart Computer Group where the EA equals "Non-Compliant" to identify devices needing remediation.
Microsoft Intuneโ
๐ Deploying Scripts in Intune
In the Intune admin center, navigate to Devices โ macOS โ Shell scripts.
Click "Add" and upload your compliance script file.
Set "Run script as signed-in user" to No (runs as root). Set "Hide script notifications" based on your preference.
In "Script arguments", enter --check for audit or --fix for remediation.
Assign the script to device groups. Set the run frequency (once, every day, etc.).
Other MDMsโ
Scripts work with any MDM that supports running shell scripts on macOS:
| ๐ | Kandji | Use Custom Scripts library item. Set to run on a schedule or once. |
| ๐ฃ | Mosyle | Use Custom Commands. Upload script and assign to device groups. |
| โซ | Workspace ONE | Use Scripts feature. Configure trigger and assignment. |
What Happens on the Macโ
When your MDM runs the compliance script, here's the sequence:
The MDM agent on the Mac (like Jamf binary or Intune agent) receives instruction to run the script.
Most compliance checks need root access to read system settings. The MDM agent runs the script as root.
Each rule's check command runs. Commands like defaults read, systemsetup, or csrutil status query current settings.
Pass/fail results write to /Library/Logs/ and stdout. Your MDM captures this output.
Remediation commands execute to change settings. This might run defaults write, enable services, or modify system files.
The script returns 0 (success) or non-zero (failures detected). Some MDMs use this to determine policy success.
Audit Plist and Exemptionsโ
The compliance script checks for an "audit plist" file that tracks exemptions. If a rule is marked exempt, the script skips it.
How Exemptions Workโ
# The script checks the audit plist before running each rule
exempt_reason=$(defaults read "/Library/Preferences/org.{baseline}.audit.plist" \
os_firewall_log_enable_exempt_reason 2>/dev/null)
if [[ -n "$exempt_reason" ]]; then
logmessage "os_firewall_log_enable: EXEMPT - $exempt_reason"
return 0 # Skip this rule
fi
Why use exemptions?
- A device has a legitimate reason to deviate (for example, testing system or specific application requirement)
- Document the reason in the audit plist so auditors can see why the rule was skipped
Deploying the Audit Plistโ
The audit plist can be deployed as:
- A
.plistfile copied to/Library/Preferences/ - A
.mobileconfigprofile pushed via MDM (recommended because it ensures the file is managed)
Best Practicesโ
Run the script manually on test Macs before deploying via MDM. Verify the checks work correctly and fixes don't break anything.
Deploy with --check first to understand your current compliance posture. Review results before running --fix.
Run the audit script on a recurring schedule (daily or weekly) to continuously monitor compliance status.
Create MDM groups based on EA results. Scope remediation policies only to non-compliant devices.
Settings can change over time. Regular audits catch when devices drift out of compliance.