Mac Admins’ new favorite, MDM-agnostic, “set-it-and-forget-it” end-user messaging for Apple’s Declarative Device Management-enforced macOS update deadlines

Overview
While Apple’s Declarative Device Management (DDM) provides Mac Admins a powerful method to enforce macOS updates, its built-in notification tends to be too subtle for most Mac Admins:

DDM OS Reminder evaluates the most recent EnforcedInstallDate and setPastDuePaddedEnforcementDate entries in /var/log/install.log, then leverages a swiftDialog-enabled script and LaunchDaemon pair to dynamically deliver a more prominent end-user message of when the user’s Mac needs to be updated to comply with DDM-enforced macOS update deadlines:


Features

Mac Admins can configure
daysBeforeDeadlineBlurscreento control how many days before the DDM-specified deadline the screen blurs when displaying your customized message:
- Customizable: Easily customize the swiftDialog message’s title, message, icon, and button text to fit your organization’s needs by editing the provided
DDM-OS-Reminder End-user Message.zshscript. (See Step A.) - Set-it-and-forget-it: Once installed, the LaunchDaemon executes your customized
ddmOSReminder.zshscript, which automatically checks the installed version of macOS against the DDM-enforced macOS version twice daily and displays your customized message if an update is required. (See Step B.) - Deadline Awareness: Each time a DDM-enforced macOS version and deadline is updated via your MDM solution, the message will dynamically include an updated countdown to the deadline, creating a sense of urgency for end-users to update their Macs.
- Tastefully Intrusive: The message is designed to be informative without being overly disruptive — first checking for the user’s Display Sleep Assertions — allowing users to continue their work while being reminded of the need to update.
- Easy Installation: A new
assembleDDMOSReminder.zshscript makes it easy to deploy via any MDM solution so you can quickly roll out across your entire organization. - Logging: The script logs its actions to your specified log file, allowing Mac Admins to monitor its activity and troubleshoot if necessary.
76-second Test-drive
76-second Test-drive Steps
Complete the following test-drive on a non-production Mac, previously configured with a pending Declarative Device Management-enforced macOS update; caveat emptor.
- Open Terminal and change to your Downloads directory
cd ~/Downloads
- Clone the DDM OS Reminder repository to your Downloads directory:
git clone https://github.com/dan-snelson/DDM-OS-Reminder.git
- Change to the DDM-OS-Reminder > Resources directory:
cd DDM-OS-Reminder/Resources
- Assemble the
ddmOSReminder.zshscript:
zsh assembleDDMOSReminder.zsh
- Execute the assembled
ddmOSReminder.zshscript (with elevated privileges)
sudo zsh ddmOSReminder.Assembled.*.zsh
- Review the client-side logs:
tail -f /var/log/org.churchofjesuschrist.log
- Kickstart the LaunchDaemon:
launchctl kickstart -kp system/org.churchofjesuschrist.dor
Implementation
A. Customize DDM-OS-Reminder End-user Message.zsh for your organization
If you didn’t take the 76-second Test-drive, please do so now to download the GitHub repository to your Downloads folder.
The GitHub repository includes a sample DDM-OS-Reminder End-user Message.zsh script which you can use to jump-start your user-facing messaging (and which you’ll later insert into the ddmOSReminder.zsh script, but not deploy to your endpoints; see Step B).

You’ll want to first update the Organization Variables for your environment:
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Organization Variables # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Script Human-readable Name humanReadableScriptName="DDM OS Reminder End-user Message" # Organization's Script Name organizationScriptName="dorm" # Organization's Days Before Deadline Blur Screen daysBeforeDeadlineBlurscreen="3"
Please note that when testing, the script includes logic in the
installedOSvsDDMenforcedOSfunction to display only when a pending update is available and the recommendation is to test on a purposely outdated Mac (and to comment-out the script’ssleep "${sleepSeconds}").
Script output for a pending DDM macOS update
root@Dan ~ # zsh /Users/dan/Downloads/DDM-OS-Reminder\ End-user\ Message.zsh dorm (1.3.0): 2025-11-09 14:37:44 - [PRE-FLIGHT] Created specified scriptLog: /var/log/org.churchofjesuschrist.log dorm (1.3.0): 2025-11-09 14:37:44 - [PRE-FLIGHT] ### # DDM OS Reminder End-user Message (1.3.0) # https://snelson.us #### dorm (1.3.0): 2025-11-09 14:37:44 - [PRE-FLIGHT] Initiating … dorm (1.3.0): 2025-11-09 14:37:44 - [PRE-FLIGHT] Complete dorm (1.3.0): 2025-11-09 14:37:44 - [NOTICE] Installed OS Version: 26.0.1 dorm (1.3.0): 2025-11-09 14:37:44 - [INFO] DDM-enforced OS Version: 26.1 dorm (1.3.0): 2025-11-09 14:37:44 - [INFO] DDM-enforced OS Version Deadline: 2025-11-14 dorm (1.3.0): 2025-11-09 14:37:44 - [NOTICE] Update Required dorm (1.3.0): 2025-11-09 14:37:44 - [NOTICE] Login Trigger Pause: Random 30 to 90 seconds dorm (1.3.0): 2025-11-09 14:37:44 - [INFO] Pausing for 78 seconds … dorm (1.3.0): 2025-11-09 14:39:02 - [NOTICE] Downloading icon from 'https://ics.services.jamfcloud.com/icon/hash_4555d9dc8fecb4e2678faffa8bdcf43cba110e81950e07a4ce3695ec2d5579ee' … dorm (1.3.0): 2025-11-09 14:39:03 - [NOTICE] Display Dialog Window dorm (1.3.0): 2025-11-09 14:39:15 - [INFO] Return Code: 0 dorm (1.3.0): 2025-11-09 14:39:15 - [NOTICE] User clicked Open Software Update dorm (1.3.0): 2025-11-09 14:39:16 - [QUIT] Exiting … dorm (1.3.0): 2025-11-09 14:39:16 - [QUIT] Shine on, you crazy diamond!
Script output on an up-to-date Mac
root@Dan ~ # zsh /Users/dan/Downloads/DDM-OS-Reminder\ End-user\ Message.zsh dorm (1.3.0): 2025-11-09 14:34:25 - [PRE-FLIGHT] ### # DDM OS Reminder End-user Message (1.3.0) # https://snelson.us #### dorm (1.3.0): 2025-11-09 14:34:25 - [PRE-FLIGHT] Initiating … dorm (1.3.0): 2025-11-09 14:34:25 - [PRE-FLIGHT] Complete dorm (1.3.0): 2025-11-09 14:34:25 - [NOTICE] Installed OS Version: 26.1 dorm (1.3.0): 2025-11-09 14:34:26 - [INFO] DDM-enforced OS Version: 26.1 dorm (1.3.0): 2025-11-09 14:34:26 - [NOTICE] Up-to-date
B. Assemble ddmOSReminder.zsh for your organization
If you didn’t take the 76-second Test-drive, please do so now to download the GitHub repository to your Downloads folder.
The GitHub repository includes a new assembleDDMOSReminder.zsh script which combines the logic for creating the “set-in-and-forget-it” LaunchDaemon and your customized messaging (from Step A):
cd Resources zsh assembleDDMOSReminder.zsh
Please remember to also update the Organization Variables for your environment in the assembled ddmOSReminder.zsh script:
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# Organization Variables
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# Organization's Reverse Domain Name Notation (i.e., com.company.division)
reverseDomainNameNotation="org.churchofjesuschrist"
# Script Human-readabale Name
humanReadableScriptName="DDM OS Reminder"
# Organization's Script Name
organizationScriptName="dor"
# Organization's Directory (i.e., where your client-side scripts reside)
organizationDirectory="/Library/Management/org.churchofjesuschrist"
# LaunchDaemon Name & Path
launchDaemonName="${reverseDomainNameNotation}.${organizationScriptName}.plist"
launchDaemonPath="/Library/LaunchDaemons/${launchDaemonName}"
You’ll also most likely want to adjust the LaunchDaemon’s StartCalendarInterval for your environment (and launchd.info may prove helpful).
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
#
# CREATE LAUNCHDAEMON
#
# The following function creates the LaunchDaemon which executes the previously created
# client-side DDM OS Reminder script.
#
# We've elected to prompt our users twice a day (8 a.m. and 4 p.m.) to ensure they see the message.
#
# NOTE: Leave a full return at the end of the content before the "ENDOFLAUNCHDAEMON" line.
#
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
function createLaunchDaemon() {
notice "Create LaunchDaemon"
logComment "Creating '${launchDaemonPath}' …"
(
cat <<ENDOFLAUNCHDAEMON
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>${launchDaemonLabel}</string>
<key>UserName</key>
<string>root</string>
<key>ProgramArguments</key>
<array>
<string>/bin/zsh</string>
<string>${organizationDirectory}/${organizationScriptName}.zsh</string>
</array>
<key>RunAtLoad</key>
<true/>
<key>EnvironmentVariables</key>
<dict>
<key>PATH</key>
<string>/usr/bin:/bin:/usr/sbin:/sbin:/usr/local:/usr/local/bin</string>
</dict>
<key>StartCalendarInterval</key>
<array>
<dict>
<key>Hour</key>
<integer>8</integer>
<key>Minute</key>
<integer>0</integer>
</dict>
<dict>
<key>Hour</key>
<integer>16</integer>
<key>Minute</key>
<integer>0</integer>
</dict>
</array>
<key>StandardErrorPath</key>
<string>${scriptLog}</string>
<key>StandardOutPath</key>
<string>${scriptLog}</string>
</dict>
</plist>
ENDOFLAUNCHDAEMON
) > "${launchDaemonPath}"
logComment "Setting permissions for '${launchDaemonPath}' …"
chmod 644 "${launchDaemonPath}"
chown root:wheel "${launchDaemonPath}"
logComment "Loading '${launchDaemonLabel}' …"
launchctl bootstrap system "${launchDaemonPath}"
launchctl start "${launchDaemonPath}"
}
C. Add your assembled ddmOSReminder.zsh script to your MDM server
When adding your assembled ddmOSReminder.zsh script to your Jamf Pro server, specify the following for Settings > Computer Management > Scripts > Options > Parameter Labels > Parameter 4
- Parameter 4:
Configuration Files to Reset (i.e., None (blank) | All | LaunchDaemon | Script | Uninstall )

For other MDMs, you can simply “hard-code” the desired setting for the resetConfiguration variable from the following available options:
- None
blank AllLaunchDaemonScriptUninstall
# Parameter 4: Configuration Files to Reset (i.e., None (blank) | All | LaunchDaemon | Script | Uninstall )
resetConfiguration="${4:-"All"}"
D. Create a DDM OS Reminder MDM policy
F. Add Extension Attributes (optional)
While the following Extension Attributes were created for and tested on Jamf Pro, they most likely can be adapted to other MDMs. (For adaptation assistance, help is available on the Mac Admins Slack #ddm-os-reminders channel, or you can open an issue.)
Resources
- JamfEA-DDM-OS-Reminder-User-Clicks.zsh: Reports the user’s button clicks for the DDM OS Reminder message.
2025-11-09 02:53:37 dan clicked Remind Me Later 2025-11-09 02:55:28 dan clicked Open Software Update 2025-11-09 03:01:11 dan clicked Remind Me Later 2025-11-09 03:11:32 dan clicked Remind Me Later 2025-11-09 03:48:27 dan clicked KB0054571
- JamfEA-Pending_OS_Update_Date.zsh: Reports the date of a pending DDM-enforced macOS update.
2025-11-14 18:00:00
- JamfEA-Pending_OS_Update_Version.zsh: Reports the version of a pending DDM-enforced macOS update.
26.1
G. Upgrading
The following is one method of using Visual Studio Code to upgrade your customized script to the latest version.
- Download the latest assembled version from the Resources directory on GitHub
- You may also wish to review the various branches for pre-release versions
- Make note of
scriptVersionin the freshly downloaded script - Using a backup of your current, known-working version, open both files in VS Code
- Click the Explorer button (Command-Shift-E)
- If necessary, enable “Open Editors” in Explorer’s options
- Individually right-click the files listed in the Open Editors to begin the comparison
- Select for Compare: The freshly downloaded script
- Compare with Selected: Your duplicated, renamed, customized script
- Close the Explorer
- Review each difference — shown in red — then click the right-facing arrow to replace the code in your duplicated, renamed, customized script with the code from the freshly downloaded script
- Notes:
- You can safely ignore any LaunchDaemon-related differences
- Enabling View > Word Wrap can interfere with code comparison
- Notes:
- Test
- Deploy
Support
Community-supplied, best-effort support is available on the Mac Admins Slack (free, registration required) #ddm-os-reminders channel, or you can open an issue.


Pingback:DDM OS Reminder (1.2.0) - Dan K. Snelson