Yet another maintenance release of Mac Admins’ new favorite, MDM-agnostic, “set-it-and-forget-it” end-user reminder for Apple’s Declarative Device Management-enforced macOS update deadlines, with a new allowlist for more robust meeting detection, dark‑mode overlay icon support, and a significantly improved, interactive pre-deployment assembly script
Overview
While Apple’s Declarative Device Management (DDM) provides Mac Admins with a powerful way to enforce macOS updates, its built-in notification is often too subtle for most administrators:


DDM OS Reminder evaluates the most recent EnforcedInstallDate and setPastDuePaddedEnforcementDate entries in /var/log/install.log, and then leverages a swiftDialog-enabled script plus a LaunchDaemon to deliver a more prominent end-user dialog that reminds users to update their Mac 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 reminder dialog
- Customizable: Easily customize the reminder dialog’s title, message, icons and button text to fit your organization’s requirements by distributing a Configuration Profile via any MDM solution.
- Easy Installation: The
assemble.zshscript makes it easy to deploy your reminder dialog and display frequency customizations via any MDM solution, enabling quick rollout of DDM OS Reminder organization-wide. - Set-it-and-forget-it: Once configured and installed, a LaunchDaemon displays your customized reminder dialog — automatically checking the installed macOS version against the DDM-required version — to remind users if an update is required.
- Deadline Awareness: Whenever a DDM-enforced macOS version or its deadline is updated via your MDM solution, the reminder dialog dynamically updates the countdown to both the deadline and required macOS version to drive timely compliance.
- Intelligently Intrusive: The reminder dialog is designed to be informative without being disruptive. Before displaying, it checks for active display-sleep assertions from an allowlist of approved meeting apps, helping users stay productive while still being reminded to update.
- Logging: The script logs its actions to your specified log file, allowing Mac Admins to monitor its activity and troubleshoot as necessary.
- Demonstration Mode: A built-in
demomode allows Mac Admins to test the appearance and functionality of the reminder dialog with ease.
Version
3.0.0, currently in its first alpha release, will support multiple languages



Implementation
1. Test Deployment
Jumpstart your DDM OS Reminder implementation by first conducting a test deployment on a non-production Mac which has swiftDialog installed. (Ideally, use a non-production Mac which is already in-scope of a pending Declarative Device Management-enforced macOS update from your MDM server.)
- Visit the DDM OS Reminder repository on GitHub
- Download the
mainbranch by selecting Code > Download ZIP

- In an elevated Terminal session, change to the downloaded
DDM-OS-Reminder-maindirectory
cd ~/Downloads/DDM-OS-Reminder-main
- Execute the
reminderDialog.zshscript indemomode:
zsh reminderDialog.zsh demo

- Review the reminder dialog and interact with each of its buttons, re-executing
zsh reminderDialog.zsh demoas required - Review the script’s output:
root@XDT8675309 dan # cd /Users/dan/Downloads/DDM-OS-Reminder-main root@XDT8675309 DDM-OS-Reminder-main # zsh reminderDialog.zsh demo dorm (2.4.0): 2026-02-07 17:38:28 - [PRE-FLIGHT] Created specified scriptLog: /var/log/org.churchofjesuschrist.log dorm (2.4.0): 2026-02-07 17:38:28 - [PRE-FLIGHT] ### # DDM OS Reminder End-user Message (2.4.0) # http://snelson.us/ddm ### dorm (2.4.0): 2026-02-07 17:38:28 - [PRE-FLIGHT] Initiating … dorm (2.4.0): 2026-02-07 17:38:28 - [PRE-FLIGHT] Check for Logged-in System Accounts … dorm (2.4.0): 2026-02-07 17:38:28 - [PRE-FLIGHT] Current Logged-in User: dan dorm (2.4.0): 2026-02-07 17:38:28 - [PRE-FLIGHT] Current Logged-in User First Name (ID): Dan (502) dorm (2.4.0): 2026-02-07 17:38:28 - [PRE-FLIGHT] No client-side preferences found; using script-defined defaults dorm (2.4.0): 2026-02-07 17:38:28 - [PRE-FLIGHT] Complete dorm (2.4.0): 2026-02-07 17:38:28 - [NOTICE] Demo mode enabled dorm (2.4.0): 2026-02-07 17:38:28 - [NOTICE] Check dan’s Display Sleep Assertions dorm (2.4.0): 2026-02-07 17:38:28 - [INFO] Acceptable assertion application names (allowlist): MSTeams zoom.us Webex dorm (2.4.0): 2026-02-07 17:38:28 - [INFO] dan’s Display Sleep Assertion has ended after 0 minute(s). dorm (2.4.0): 2026-02-07 17:38:28 - [NOTICE] No active Display Sleep Assertions detected; proceeding … dorm (2.4.0): 2026-02-07 17:38:28 - [NOTICE] Light mode detected; using standard overlay icon dorm (2.4.0): 2026-02-07 17:38:28 - [NOTICE] Processing overlay icon from 'https://use2.ics.services.jamfcloud.com/icon/hash_2d64ce7f0042ad68234a2515211adb067ad6714703dd8ebd6f33c1ab30354b1d' dorm (2.4.0): 2026-02-07 17:38:28 - [INFO] Overlay icon appears to be a remote URL; downloading with curl dorm (2.4.0): 2026-02-07 17:38:29 - [INFO] Successfully downloaded overlay icon dorm (2.4.0): 2026-02-07 17:38:30 - [NOTICE] Display Reminder Dialog to dan with additional options: --ontop dorm (2.4.0): 2026-02-07 17:38:44 - [INFO] Return Code: 0 dorm (2.4.0): 2026-02-07 17:38:44 - [NOTICE] dan clicked Open Software Update dorm (2.4.0): 2026-02-07 17:38:44 - [NOTICE] Checking if System Settings is open … dorm (2.4.0): 2026-02-07 17:38:44 - [INFO] System Settings is open; Telling System Settings to make a guest appearance … dorm (2.4.0): 2026-02-07 17:38:45 - [QUIT] Exiting … dorm (2.4.0): 2026-02-07 17:38:45 - [QUIT] Gambling only pays when you’re winning!
- Simulate the installation of a
.plistclient-side by manually copyingsample.plistto one of its expected locations with its expected filename:
cp -v Resources/sample.plist /Library/Preferences/org.churchofjesuschrist.dorm.plist
- Re-execute
zsh reminderDialog.zsh demoand confirm you now observe the word “Sample” in various places in the reminder dialog, as configured insample.plist:

2. Basic Deployment
A basic deployment of DDM OS Reminder starts by specifying your organization’s Reverse Domain Name Notation value when prompted by the
assemble.zshscript
- Generate customized deployment artifacts for your organization by using the
assemble.zshscript, specifying your organization’s Reverse Domain Name Notation (i.e.,com.company) when prompted:
zsh assemble.zsh --help
root@XDT8675309 DDM-OS-Reminder-main # zsh assemble.zsh --help
===============================================================
🧩 Assemble DDM OS Reminder (2.4.0)
===============================================================
Full Paths:
Reminder Dialog: /Users/dan/Downloads/DDM-OS-Reminder-main/reminderDialog.zsh
LaunchDaemon Management: /Users/dan/Downloads/DDM-OS-Reminder-main/launchDaemonManagement.zsh
Working Directory: /Users/dan/Downloads/DDM-OS-Reminder-main
Resources Directory: /Users/dan/Downloads/DDM-OS-Reminder-main/Resources
🔍 Checking Reverse Domain Name Notation …
Reminder Dialog (reminderDialog.zsh):
reverseDomainNameNotation = org.churchofjesuschrist
organizationScriptName = dorm
LaunchDaemon Management (launchDaemonManagement.zsh):
reverseDomainNameNotation = org.churchofjesuschrist
organizationScriptName = dor
Usage:
zsh assemble.zsh [RDNN] [--lane dev|test|prod] [--interactive] [--help]
Options:
--lane <dev|test|prod> Select deployment mode
--interactive Prompt for IT support and branding values
--help, -h Show this help
- A new
--interactiveoption helps to generate a fully customized.plistand.mobileconfigwhen assemblying the deployable script
zsh assemble.zsh us.snelson --interactive
root@XDT8675309 DDM-OS-Reminder-main # zsh assemble.zsh us.snelson --interactive
===============================================================
🧩 Assemble DDM OS Reminder (2.4.0)
===============================================================
Full Paths:
Reminder Dialog: /Users/dan/Downloads/DDM-OS-Reminder-main/reminderDialog.zsh
LaunchDaemon Management: /Users/dan/Downloads/DDM-OS-Reminder-main/launchDaemonManagement.zsh
Working Directory: /Users/dan/Downloads/DDM-OS-Reminder-main
Resources Directory: /Users/dan/Downloads/DDM-OS-Reminder-main/Resources
🔍 Checking Reverse Domain Name Notation …
Reminder Dialog (reminderDialog.zsh):
reverseDomainNameNotation = org.churchofjesuschrist
organizationScriptName = dorm
LaunchDaemon Management (launchDaemonManagement.zsh):
reverseDomainNameNotation = org.churchofjesuschrist
organizationScriptName = dor
📥 RDNN provided via command-line argument: 'us.snelson'
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Using 'us.snelson' as the Reverse Domain Name Notation
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
IT Support & Branding (Interactive)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Support Team Name [IT Support] (or ‘X’ to exit): Support
Support Team Phone [+1 (801) 555-1212] (or ‘X’ to exit): +1 (937) 555-1212
Support Team Email [rescue@snelson.us] (or ‘X’ to exit):
Support Team Website [https://support.snelson.us] (or ‘X’ to exit):
Support KB Title [Update macOS on Mac] (or ‘X’ to exit): KB8675309
Info Button Action [https://support.snelson.us/KB8675309] (or ‘X’ to exit):
Support KB Markdown Link [[KB8675309](https://support.snelson.us/KB8675309)] (or ‘X’ to exit):
Overlay Icon URL (Light) [https://use2.ics.services.jamfcloud.com/icon/hash_2d64ce7f0042ad68234a2515211adb067ad6714703dd8ebd6f33c1ab30354b1d] (or ‘X’ to exit): https://usw2.ics.services.jamfcloud.com/icon/hash_4804203ac36cbd7c83607487f4719bd4707f2e283500f54428153af17da082e2
Overlay Icon URL (Dark) [https://use2.ics.services.jamfcloud.com/icon/hash_d3a3bc5e06d2db5f9697f9b4fa095bfecb2dc0d22c71aadea525eb38ff981d39] (or ‘X’ to exit): https://usw2.ics.services.jamfcloud.com/icon/hash_4804203ac36cbd7c83607487f4719bd4707f2e283500f54428153af17da082e2
Swap Overlay and Logo (YES/NO) [NO] (or ‘X’ to exit):
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Select Deployment Mode:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
1) Development - Keep placeholder text for local testing
2) Testing - Replace placeholder text with 'TEST' for staging
3) Production - Remove placeholder text for clean deployment
[Press ‘X’ to exit]
Enter mode [1/2/3]: 2
📦 Deployment Mode: test
🔧 Inserting reminderDialog.zsh into launchDaemonManagement.zsh …
✅ Assembly complete [2026-02-08-180243]
→ Artifacts/ddm-os-reminder-assembled-2026-02-08-180243.zsh
🔁 Updating reverseDomainNameNotation to 'us.snelson' in assembled script …
🔍 Performing syntax check on 'Artifacts/ddm-os-reminder-assembled-2026-02-08-180243.zsh' …
✅ Syntax check passed.
🗂 Generating LaunchDaemon plist …
🗂 Creating us.snelson.dorm plist from /Users/dan/Downloads/DDM-OS-Reminder-main/Resources/sample.plist …
🔧 Updating internal plist content …
🧪 Testing mode: replacing placeholder text → 'TEST'
🔧 Applying IT support and branding values …
→ Artifacts/us.snelson.dorm-2026-02-08-180243-test.plist
🧩 Generating Configuration Profile (.mobileconfig) …
→ Artifacts/us.snelson.dorm-2026-02-08-180243-test-unsigned.mobileconfig
🔍 Performing syntax check on 'Artifacts/us.snelson.dorm-2026-02-08-180243-test-unsigned.mobileconfig' …
✅ Profile syntax check passed.
🔁 Renaming assembled script …
🔁 Updating scriptLog path based on RDNN …
🏁 Done.
Deployment Artifacts:
Assembled Script: Artifacts/ddm-os-reminder-us.snelson-2026-02-08-180243-test.zsh
Organizational Plist: Artifacts/us.snelson.dorm-2026-02-08-180243-test.plist
Configuration Profile: Artifacts/us.snelson.dorm-2026-02-08-180243-test-unsigned.mobileconfig
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
⚠️ Important Next Steps:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Testing Artifacts Generated:
- All placeholder text replaced with 'TEST'
- Suitable for staging/QA environments
- NOT suitable for production use
===============================================================
- Carefully review each deployment artifact and distribute the appropriate files to a single test Mac via your MDM.
- Monitor the client-side log file, using the following as an example (substitute your organization’s Reverse Domain Name Notation and use Control-C to break):
tail -f /var/log/org.churchofjesuschrist.log
- Kickstart the DDM OS Reminder LaunchDaemon, using the following as an example (substitute your organization’s Reverse Domain Name Notation):
launchctl kickstart -kp system/org.churchofjesuschrist.dor
- In your preferred code editor — being careful to maintain the XML-escaped text — modify the various
stringvalues in your customized.plistor.mobileconfigto suit your organization:- Logging
ScriptLog
- Reminder Timing
DaysBeforeDeadlineDisplayReminderDaysBeforeDeadlineBlurscreenDaysBeforeDeadlineHidingButton2DaysOfExcessiveUptimeWarningMinimumDiskFreePercentageMeetingDelay
- Branding
OrganizationOverlayIconURLSwapOverlayAndLogo
- Support
SupportTeamNameSupportTeamPhoneSupportTeamEmailSupportTeamWebsiteSupportKBInfoButtonActionSupportKBURL
- Dialog
TitleButton1TextButton2Text(set to<string></string>to suppress displaying)InfoButtonText(set tohideto supress displaying)ExcessiveUptimeWarningMessageDiskSpaceWarningMessageMessage
- Infobox
InfoBox
- Help
HelpMessageHelpImage(set tohideto supress displaying)
- Logging
- Distribute the updated
.plistor.mobileconfigto your test Mac - Kickstart the DDM OS Reminder LaunchDaemon, using the following as an example (substitute your organization’s Reverse Domain Name Notation):
launchctl kickstart -kp system/org.churchofjesuschrist.dor
- Monitor the client-side log file, using the following as an example (substitute your organization’s Reverse Domain Name Notation and use Control-C to break):
tail -f /var/log/org.churchofjesuschrist.log
3. Advanced Deployment
An advanced deployment of DDM OS Reminder leverages a customized LaunchDaemon for fine-grained control of when the reminder dialog is displayed to your users
- In your preferred code editor, modify the
StartCalendarIntervalinlaunchDaemonManagement.zshto match your organization’s requirements; launchd.info is a great reference
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
- Next, use your preferred code editor to modify the hard-coded random delay in
reminderDialog.zshto match the modifications you made in Step 1.
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# If Update Required, Display Dialog Window (respecting Display Reminder threshold)
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
if [[ "${versionComparisonResult}" == "Update Required" ]]; then
# -------------------------------------------------------------------------
# Deadline window and periodic reminder logic (thanks for the suggestion, @kristian!)
# -------------------------------------------------------------------------
quietPeriodSeconds=4560 # 76 minutes (60 minutes + margin)
periodicReminderDays=28 # 28 days
periodicReminderSeconds=$(( periodicReminderDays * 86400 ))
# Look for the most recent user interaction by Return Code
# Return Code 0: User clicked Button 1 (Open Software Update)
# Return Code 2: User clicked Button 2 (Remind Me Later)
# Return Code 3: User clicked Info Button
# Return Code 4: User allowed timer to expire
# Return Code 10: User quit dialog with keyboard shortcut
# These are the events that indicate the user consciously dismissed / acknowledged the dialog
lastInteraction=$(grep -E '\[INFO\].*Return Code: (0|2|3|4|10)' "${scriptLog}" | \
tail -1 | \
sed -E 's/^[^:]+: ([0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}).*/\1/')
if (( ddmVersionStringDaysRemaining > daysBeforeDeadlineDisplayReminder )); then
# Outside the deadline window; check if we should display initial/periodic reminder
if [[ -z "${lastInteraction}" ]]; then
# No interaction history; display the initial reminder dialog
notice "No reminder interaction history found; displaying initial reminder dialog"
else
# Validate the extracted timestamp matches expected format
if [[ "${lastInteraction}" =~ ^[0-9]{4}-[0-9]{2}-[0-9]{2}\ [0-9]{2}:[0-9]{2}:[0-9]{2}$ ]]; then
nowEpoch=$(date +%s)
lastEpoch=$( date -j -f "%Y-%m-%d %H:%M:%S" "${lastInteraction}" +"%s" 2>/dev/null )
if [[ -n "${lastEpoch}" ]]; then
delta=$(( nowEpoch - lastEpoch ))
if (( delta >= periodicReminderSeconds )); then
# Last interaction was 28+ days ago; display periodic reminder
daysAgo=$(( delta / 86400 ))
notice "Last reminder interaction was ${daysAgo} day(s) ago; displaying periodic reminder dialog"
else
# Last interaction was within 28 days; skip
daysAgo=$(( delta / 86400 ))
quitOut "Deadline still ${ddmVersionStringDaysRemaining} days away and last reminder was ${daysAgo} day(s) ago; exiting quietly."
quitScript "0"
fi
else
info "Could not parse last interaction timestamp; proceeding with display"
fi
else
info "Last interaction timestamp format invalid; proceeding with display"
fi
fi
else
notice "Within ${daysBeforeDeadlineDisplayReminder}-day reminder window; proceeding …"
fi
# -------------------------------------------------------------------------
# Short quiet period: skip dialog if user interacted very recently
# -------------------------------------------------------------------------
if [[ -n "${lastInteraction}" ]]; then
# Validate the extracted timestamp matches expected format
if [[ "${lastInteraction}" =~ ^[0-9]{4}-[0-9]{2}-[0-9]{2}\ [0-9]{2}:[0-9]{2}:[0-9]{2}$ ]]; then
nowEpoch=$(date +%s)
lastEpoch=$( date -j -f "%Y-%m-%d %H:%M:%S" "${lastInteraction}" +"%s" 2>/dev/null )
if [[ -n "${lastEpoch}" ]]; then
delta=$(( nowEpoch - lastEpoch ))
if (( delta < quietPeriodSeconds )); then
minutesAgo=$(( delta / 60 ))
quitOut "User last interacted with reminder dialog ${minutesAgo} minute(s) ago; exiting quietly."
quitScript "0"
fi
fi
fi
fi
# -------------------------------------------------------------------------
# Confirm the currently logged-in user is “available” to be reminded
# -------------------------------------------------------------------------
if [[ "${ddmVersionStringDaysRemaining}" -gt 1 ]]; then
if checkUserDisplaySleepAssertions; then
notice "No active Display Sleep Assertions detected; proceeding …"
else
quitOut "Presentation still active after ${meetingDelay} minutes; exiting quietly."
quitScript "0"
fi
else
info "Deadline is within 24 hours; ignoring ${loggedInUser}’s Display Sleep Assertions; proceeding …"
fi
# -------------------------------------------------------------------------
# Random pause depending on launch context (hourly vs login)
# -------------------------------------------------------------------------
currentHour=$(( $(date +%H) ))
currentMinute=$(( $(date +%M) ))
if (( currentHour == 8 || currentHour == 16 )) && (( currentMinute == 0 )); then
notice "Daily Trigger Pause: Random 0 to 20 minutes"
sleepSeconds=$(( RANDOM % 1200 ))
else
notice "Login Trigger Pause: Random 30 to 90 seconds"
sleepSeconds=$(( 30 + RANDOM % 61 ))
fi
if (( sleepSeconds >= 60 )); then
(( pauseMinutes = sleepSeconds / 60 ))
(( pauseSeconds = sleepSeconds % 60 ))
if (( pauseSeconds == 0 )); then
humanReadablePause="${pauseMinutes} minute(s)"
else
humanReadablePause="${pauseMinutes} minute(s), ${pauseSeconds} second(s)"
fi
else
humanReadablePause="${sleepSeconds} second(s)"
fi
info "Pausing for ${humanReadablePause} …"
sleep "${sleepSeconds}"
# -------------------------------------------------------------------------
# Continue with normal processing
# -------------------------------------------------------------------------
updateRequiredVariables
displayReminderDialog --ontop
else
notice "Version Comparison Result: ${versionComparisonResult}"
fi
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# Exit
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
quitScript "0"
- Execute the
assemble.zshscript to insert your customizedreminderDialog.zshscript into your customizedlaunchDaemonManagement.zshscript and to create the deployment artifacts - Carefully review each deployment artifact and distribute the appropriate files to a single test Mac via your MDM
- Kickstart the DDM OS Reminder LaunchDaemon
- Monitor the client-side log file
4. Upgrading
Author’s Highly Opinionated Thought: There are enough changes in version
2.xthat it’s probably easier to just start-from-scratch — optionally uninstalling earlier versions — rather than attempting to update from a previous version of DDM OS Reminder.
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
5. Resources
Diagrams
AI-generated visual diagrams to augment this documentation are available at Diagrams/README.md.
Scripts
- Assemble combined, deployable artifacts of your customized scripts
- Create Self-extracting encodes the most recently assembled script for easier deployment with some MDMs
- Create
.plistcreates.plistand.mobileconfig, based on your customizations toreminderDialog.zsh - Extension Attributes were created for and tested on Jamf Pro and can most likely be adapted for other MDMs
- Jamf-getDDMstatusFromCSV.zsh; See: DDM Status from .CSV (1.0.0)
Configuration Profile
Special thanks to Max Sundell for his write-up: Create and deploy a macOS configuration profile (.mobileconfig)
Testing Tips
The following have proved helpful during development and testing:
XTRACE
Execute the client-side reminder dialog script — under xtrace with a custom prompt — using the following as an example, substituting your organization’s Reverse Domain Name Notation:
- Note: Once the reminder dialog appears, the last several output blocks tend to be the most informative
zsh -c 'PS4=" → "; zsh -x "$1"' -- /Library/Management/org.churchofjesuschrist/dor.zsh
Force-display
The following commands can be used to force-display the reminder dialog:
###
# Force-display reminder dialog
###
rdnn="org.churchofjesuschrist"
launchctl kickstart -kp "system/${rdnn}.dor"
tail -f "/var/log/${rdnn}.log"
###
# Atomic Log Reset
###
lines=24
keep_lines=$(($(wc -l < "/var/log/${rdnn}.log") - lines))
sed -i '' "${keep_lines}q" "/var/log/${rdnn}.log"
launchctl kickstart -kp "system/${rdnn}.dor"
tail -f "/var/log/${rdnn}.log"
###
# Nuclear Log Reset
###
truncate -s 0 "/var/log/${rdnn}.log"
launchctl kickstart -kp "system/${rdnn}.dor"
tail -f "/var/log/${rdnn}.log"
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.