Managing macOS devices in a business environment presents a unique set of challenges—particularly around user account control and system security. One of the most critical elements in Apple’s modern management stack is the concept of the Secure Token. Ensuring that macOS user accounts have proper Secure Token entitlement is essential for FileVault encryption, system upgrades, and effective remote administration.
This post explores how to Check Secure Token Status on macOS with a shell script, allowing IT professionals and MSPs to programmatically audit account status and surface potential issues in their fleet.
Background
macOS Secure Token was introduced by Apple to safeguard key operations like enabling FileVault and administering other user accounts. Essentially, it functions as a cryptographic marker that allows one account to manage or create others and access encrypted volumes. Without Secure Token, even admin-level accounts may be severely limited in function.
For IT teams and managed service providers (MSPs), verifying Secure Token status across all user accounts is not just a matter of good practice—it’s often a compliance and operability requirement. Manual checks are laborious and error-prone. This script automates the process and optionally integrates with NinjaOne, enabling real-time visibility through custom fields.
The Script
#!/usr/bin/env bash # # Description: Shows the current secure token status for all accounts on the system. # By using this script, you indicate your acceptance of the following legal terms as well as our Terms of Use at https://www.ninjaone.com/terms-of-use. # Ownership Rights: NinjaOne owns and will continue to own all right, title, and interest in and to the script (including the copyright). NinjaOne is giving you a limited license to use the script in accordance with these legal terms. # Use Limitation: You may only use the script for your legitimate personal or internal business purposes, and you may not share the script with another party. # Republication Prohibition: Under no circumstances are you permitted to re-publish the script in any script library or website belonging to or under the control of any other software provider. # Warranty Disclaimer: The script is provided “as is” and “as available”, without warranty of any kind. NinjaOne makes no promise or guarantee that the script will be free from defects or that it will meet your specific needs or expectations. # Assumption of Risk: Your use of the script is at your own risk. You acknowledge that there are certain inherent risks in using the script, and you understand and assume each of those risks. # Waiver and Release: You will not hold NinjaOne responsible for any adverse or unintended consequences resulting from your use of the script, and you waive any legal or equitable rights or remedies you may have against NinjaOne relating to your use of the script. # EULA: If you are a NinjaOne customer, your use of the script is subject to the End User License Agreement applicable to you (EULA). # # Preset Parameter: --showDisabledAccounts # Shows disabled accounts in the report. # # Preset Parameter: --multilineCustomFieldName "ReplaceMeWithYourCustomFieldName" # Specify the name of an optional multiline custom field to save the results. # # Preset Parameter: --help # Displays help text. # # Release Notes: Initial release. # Initialize the script arguments _arg_showDisabledAccounts="off" _arg_multilineCustomFieldName= # Function to print the help message print_help() { printf '\n\n%s\n\n' 'Usage: [--showDisabledAccounts|-s] [--multilineCustomFieldName|-m <arg>] [--help|-h]' printf '%s\n' 'Preset Parameter: --showDisabledAccounts' printf '\t%s\n' "Show disabled accounts in the report." printf '%s\n' 'Preset Parameter: --multilineCustomFieldName "ReplaceMeWithYourCustomFieldName"' printf '\t%s\n' "Specify the name of an optional multiline custom field to save the results." printf '\n%s\n' 'Preset Parameter: --help' printf '\t%s\n' "Displays this help menu." } # Function to display an error message and optionally print the help message die() { local _ret="${2:-1}" echo "$1" >&2 test "${_PRINT_HELP:-no}" = yes && print_help >&2 exit "${_ret}" } # Function to parse the command-line arguments parse_commandline() { while test $# -gt 0; do _key="$1" case "$_key" in --showDisabledAccounts | --showdisabledaccounts | --showDisabled | -s) _arg_showDisabledAccounts="on" ;; --multilineCustomFieldName | --multilineField | --multiline | --customField | -m) test $# -lt 2 && die "Missing value for the optional argument '$_key'." 1 _arg_multilineCustomFieldName="$2" shift ;; --help | -h) _PRINT_HELP=yes die 0 ;; *) _PRINT_HELP=yes die "FATAL ERROR: Got an unexpected argument '$1'" 1 ;; esac shift done } # Parse the command-line arguments parse_commandline "$@" # If the script form is used, override the commandline arguments with the form information. if [[ -n $multilineCustomFieldName ]]; then _arg_multilineCustomFieldName="$multilineCustomFieldName" fi if [[ -n $includeDisabledUsers && $includeDisabledUsers == "true" ]]; then _arg_showDisabledAccounts="on" fi # Get the list of user accounts with UniqueID greater than 499 (non-system accounts) userAccounts=$(dscl . -list /Users UniqueID | awk '$2 > 499 {print $1}') secureTokenAccounts= # If no user accounts are found, display an error and exit if [[ -z $userAccounts ]]; then _PRINT_HELP=no die "[Error] No user accounts were found." 1 fi # Iterate through each user account to check secure token status and other attributes for userAccount in $userAccounts; do # Check if the secure token is disabled secureTokenStatus=$(sysadminctl -secureTokenStatus "$userAccount" 2>&1 | grep "is DISABLED") if [[ -n $secureTokenStatus ]]; then secureTokenStatus="Disabled" else secureTokenStatus="Enabled" fi # Check if user account is disabled pwpolicy=$(dseditgroup -o checkmember -u "$userAccount" com.apple.access_disabled | grep "no $userAccount") authenticationAuthority=$(dscl . -read "/Users/$userAccount" AuthenticationAuthority | grep "DisabledUser") if [[ -n $pwpolicy && -z $authenticationAuthority ]]; then enabled="true" else enabled="false" fi # Skip disabled accounts if the argument to show disabled accounts is off if [[ $_arg_showDisabledAccounts == "off" && $enabled == "false" ]]; then continue fi # If the secure token status is disabled enable the alert. if [[ $secureTokenStatus == "Disabled" ]]; then accountsMissingSecureTokenFound="true" fi # Append the user account details to the secure token accounts string secureTokenAccounts+=$(printf '%s' '\n' "$userAccount" ';' "$secureTokenStatus" ';' "$enabled") done # If there are accounts missing a secure token, display an alert if [[ $accountsMissingSecureTokenFound == "true" ]]; then echo "" echo "[Alert] There are accounts that do not currently have a secure token." echo "" fi # Prepare the header and table view for displaying the results header=$(printf '%s' "Username" ';' "Secure Token" ';' "Enabled") tableView="$header" tableView+=$(printf '%s' '\n' "--------" ';' "------------" ';' "-------") tableView+="$secureTokenAccounts" tableView=$(printf '%b' "$tableView" | sed 's/$/\n/' | column -s ';' -t) # Output the table view to the activity log. printf '%b' "$tableView" echo "" echo "" # If a multiline custom field name is specified, attempt to set the custom field if [[ -n $_arg_multilineCustomFieldName ]]; then echo "" echo "Attempting to set Custom Field '$_arg_multilineCustomFieldName'..." # Formats the Secure Token data for the multiline custom field. multilineValue=$(printf '%b' "$secureTokenAccounts" | grep "\S" | awk -F ";" '{ print "Username: "$1 print "Secure Token: "$2 print "Enabled: "$3 print "" }') # Try to set the multiline custom field using ninjarmm-cli and capture the output if ! output=$(printf '%b' "$multilineValue" | /Applications/NinjaRMMAgent/programdata/ninjarmm-cli set --stdin "$_arg_multilineCustomFieldName" 2>&1); then echo "[Error] $output" >&2 EXITCODE=1 else echo "Successfully set Custom Field '$_arg_multilineCustomFieldName'!" fi fi # Checks if an error code is set and exits the script with that code. if [[ -n $EXITCODE ]]; then exit "$EXITCODE" fi
Detailed Breakdown
The provided shell script systematically checks Secure Token status for each macOS user account and outputs the findings in a readable table. Here’s how it works:
Argument Parsing
The script accepts optional flags:
- –showDisabledAccounts: Includes accounts that are disabled in the output.
- –multilineCustomFieldName: If using NinjaOne, this field’s name allows results to be saved directly into a multiline custom field.
- –help: Displays usage instructions.
User Enumeration
It filters users by checking for UniqueID > 499, effectively excluding system accounts.
Secure Token and Enabled Status Check
For each user, it checks:
- Secure Token status using sysadminctl.
- Whether the account is disabled by evaluating group membership and authentication authority.
Conditional Output
Accounts missing Secure Token are flagged, and disabled accounts are skipped unless explicitly requested.
Optional NinjaOne Integration
If a custom field is specified, the report is reformatted and passed to ninjarmm-cli to populate a field for visibility within the NinjaOne console.
Exit Codes and Errors
All errors are logged, and appropriate exit codes are returned to support automation and monitoring pipelines.
Potential Use Cases
Imagine a mid-size MSP managing 300 MacBooks across a healthcare provider’s offices. FileVault is required under HIPAA compliance, but the IT team discovers that some users can’t enable encryption. Running this script through NinjaOne reveals a handful of admin accounts missing Secure Token. The team remediates the issue by promoting another Secure Token holder to regenerate tokens. Without automation, this issue could’ve taken days to track down.
Comparisons
Manual Approach
Using sysadminctl -secureTokenStatus <user> individually is time-consuming and prone to human error.
MDM-Based Reporting
Some MDMs offer token visibility, but may not expose it account-by-account or support real-time custom field reporting.
This Script
The shell script provides:
- Granular visibility.
- Extensibility via NinjaOne.
- Command-line flexibility for automation.
It fills a gap between manual checks and high-cost enterprise MDMs.
Frequently Asked Questions
Q: Will this work on all macOS versions?
It’s compatible with macOS 10.13 and later, where Secure Token was introduced.
Q: Does this require admin privileges?
Yes. The script invokes sysadminctl and reads system directories, which require elevated permissions.
Q: What does it mean if a user is “Enabled” but has a “Disabled” Secure Token?
This means the account is active but cannot administer other accounts or enable FileVault unless another Secure Token holder grants permission.
Q: How do I fix accounts missing a Secure Token?
You’ll need a Secure Token-enabled admin account to add the token.
Implications
An account missing a Secure Token can severely hinder device management, especially when enabling FileVault or performing upgrades. Moreover, a Mac with no Secure Token holders is essentially “locked down” at a security layer invisible to many sysadmins. Regular checks like these are vital for maintaining a compliant and secure endpoint fleet.
Recommendations
- Automate the Script: Schedule regular execution via NinjaOne or launchd.
- Log Results: Keep a historical log to identify trends.
- Remediate Promptly: Immediately address any user accounts lacking Secure Token.
- Combine with Onboarding Workflows: Ensure newly created accounts receive Secure Token status upfront.
Final Thoughts
Maintaining visibility into Secure Token status is non-negotiable for robust macOS device management. This script empowers IT teams to efficiently conduct a Secure Token check on macOS with shell scripting. When paired with NinjaOne, results can be surfaced directly in the dashboard, streamlining operations and enabling rapid remediation. For any IT team looking to close security and compliance gaps, this lightweight yet powerful approach should become a key part of their toolkit.