Mac admin'ing

Things to keep in mind if using a profile to delay macOS updates

Now that Apple has removed the --ignore flag from softwareupdate, it’s recommending you use the forceDelayedSoftwareUpdates and enforcedSoftwareUpdateDelay flags (more details in Device Management Profile: Restrictions), which are supposed to, in theory, delay an updates user visibility a certain number of days after the update’s release.

The number of days delayed may not be precise

The number of days delayed is imperfect at best. I tested a 7-day delay and even 6 days after the release of the update, it was suddenly available. And here’s an example of someone last year who didn’t see an update released 10 days before, even with only a 7-day delay.

System Preferences and softwareupdate may not be in sync

In my own testing, if you delay an update but a Mac is two or more updates behind, softwareupdate -d -a or softwareupdate -l will still show an update available to download, but System Preferences > Software Update will show Your Mac is running the latest software update allowed by your administrator.

That can be a problem if you rely on a program like Nudge, which uses softwareupdate to determine whether an update is available but points users to System Preferences to do the actual update.

At this time, unless Apple makes significant changes, I wouldn’t recommend using the delay update profile settings if you have any utilities that use softwareupdate to check whether updates are available or not.

Mac admin'ing

Some basics of DEPNotify and a sample script

If you’ve been doing Munki admin’ing for a short while, you’ve probably heard people talk about DEPNotify, whose README says is “a small light weight notification app that was designed to let your users know what’s going on during a DEP enrollment.”

Aforementioned DEPNotify README is fairly comprehensive in terms of going over all the options. There’s also a project called DEPNotify-Starter, which has a sample script that’s, as of this writing, 827 lines long.

If you just want a super simple script to launch up DEPNotify and have it install some Munki stuff, I created a very, very simple sample script ( that just shows how you can use it without a ton of extra options. Once you wrap your head around that, you can always complicate it with more options.

This is what the sample script looks like in action if there’s only MunkiAdmin to install:

Mac admin'ing

If your VMWare guest macOS loses network connectivity

If you haven’t changed any settings, and suddenly the Internet connection on your VMWare guest macOS installation goes out, and shutting down the VM or rebooting the VM doesn’t help, try rebooting the host Mac. That will likely fix the problem (not sure why that problem comes up in the first place.

Mac admin'ing

Using a full macOS installer with Munki to patch macOS


This works right now, but I don’t know if this will keep working in perpetuity. As Apple plans on removing enterprise options for macOS software update points out, being able to manage patches in macOS is a moving target, and Apple is increasingly making it difficult for administrators to manage patches effectively.


Shoutout to Rod Christiansen on the MacAdmins Slack for putting this strange (but still working for now) method of patching on my radar.

Why would you want to do this?

Recently, softwareupdate has become an increasingly unreliable way to install updates. Munki 5 recently brought in some changes to have Managed Software Center nudge users to install patches via System Preferences (more details at Manual Apple Updates in Munki 5), similar to what Nudge does.

That is still the most reliable way to get patches installed, but what if you have Macs that basically sit unattended, so Nudge or a nudge-like prompt from Managed Software Center will basically be useless?

Well, apparently, you can use the full macOS installer to automate patching.

Limitations and considerations

One huge limitation, of course, is that you can probably use this method to patch only the latest macOS release. In other words, as of this writing, the latest build available for macOS 10.14.6 is 18G5033, but the latest full downloader for 10.14.6 is build 18G103. So you can’t really use an 18G103 full installer to update a Mojave client to 18G5033.

I believe using the installer to patch is the equivalent of doing a non-destructive reinstall of macOS (which you usually do in recovery mode), which means the download is considerably bigger than a regular patch download (more like 8 GB instead of 1 GB), and the install time is also longer (35-60 minutes instead of 20-30 minutes).

If the machines you’re patching are FileVault encrypted, you’ll still have to have someone be present either before or after the full macOS install to either do an authorized restart beforehand or a manual unlocking of FileVault afterwards.

Tweaks to the pkginfo

When you import something like Install macOS into Munki, munkiimport will, by default, make the installer_type into startosinstall. You may still want that installer in your Munki repo so you can upgrade Mojave clients to Catalina, so keep that pkginfo, but also make a copy of that pkginfo and tweak things a bit.

Change the installer type to be a CopyFromDMG:
            <string>Install macOS</string>

and then have a postinstall script that runs the startosinstall command after copying from the .dmg:     <key>postinstall_script</key>
# Run startosinstall
/tmp/Install\ macOS\ --agreetolicense

You’ll also want to change the name, so it’s distinct from Install_macOS_Catalina:


Might as well update the display name as well:

    <string>Update macOS Catalina</string>

The last important tweak is to throw an installcheck_script in there so Munki knows whether the patch is installed or not:     <key>installcheck_script</key>
from distutils.version import LooseVersion
import subprocess
import sys
# Desired OS build
def main():
    # Get current OS build
    cmd = [ '/usr/bin/sw_vers', '-buildVersion' ]
    p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, encoding='utf8')
    out, err = p.communicate()
    if err:
        print("ERROR: Unable to determine current OS build. Considering installed for now...")
        # Strip out extra carriage return from output
        curr_build = out.strip()
        if LooseVersion(curr_build) &gt;= LooseVersion(desired):
            print("The current build {} is already greater than or equal to {}".format(curr_build, desired))
            print("Current build {} is not yet {}".format(curr_build, desired))
if __name__ == "__main__":

Manifest Change

You may also want to add in a Munki conditional item so that only 10.15 clients are targeted.

            <string>os_vers_minor == 15</string>

What the update process looks like

    Evaluating predicate: os_vers_minor == 15
    Predicate os_vers_minor == 15 is True
    * Processing manifest item Update_macOS_Catalina for install
    Looking for detail for: Update_macOS_Catalina, version latest...
    Considering 1 items with name Update_macOS_Catalina from catalog testing
    Considering item Update_macOS_Catalina, version with minimum Munki version required
    Our Munki version is
    Considering item Update_macOS_Catalina, version with minimum os version required 10.15
    Our OS version is 10.15.5
    Considering item Update_macOS_Catalina, version with maximum os version supported 10.15.99
    Our OS version is 10.15.5
    Found Update_macOS_Catalina, version in catalog testing
    Adding Update_macOS_Catalina to list of processed installs
    Running installcheck_script for Update_macOS_Catalina
    Current build 19F96 is not yet 19F101
    installcheck_script returned 0
    Need to install Update_macOS_Catalina

    The following items will be installed or upgraded:
        + Update_macOS_Catalina-
            Updates macOS version to 10.15.5, build 19F101
         *Restart required

A key piece from /var/log/install.log: 2020-06-02 09:30:54-07 HOSTNAME softwareupdate[401]: Starting softwareupdate CLI tool
2020-06-02 09:30:54-07 HOSTNAME softwareupdated[402]: softwareupdated: Starting with build 10.15.5 (19F101)
2020-06-02 09:30:54-07 HOSTNAME softwareupdated[402]: authorizeWithEmptyAuthorizationForRights: Requesting provided rights: 1
2020-06-02 09:30:56-07 HOSTNAME softwareupdated[402]: /Library/Bundles does not exist - watching for directory creation
2020-06-02 09:30:56-07 HOSTNAME softwareupdated[402]: Previous System Version : 10.15.5 (19F96), Current System Version : 10.15.5 (19F101)

Yeah, that’s it. That whole install (not counting the download of the installer) took a full hour to complete. On another machine I tested with, it was closer to 35 minutes to complete. So, your mileage may vary.

Mac admin'ing

Force-stopping the MunkiStatus progress bar at the login window

Sometimes, the MunkiStatus progress bar over the login window can get stuck, and pressing the Stop button can take a while to halt the progress bar completely.

To kill it immediately, press Cmd-Option-Shift-Escape (this is a slight modification of the usual force-quite key combination, which is Cmd-Option-Escape).

Full credit to Yehuda Bialik and Greg Neagle for this tip. The Wiki on Bootstrapping With Munki is now updated also to include a note this keyboard shortcut.

Mac admin'ing

Using AppleScript to install macOS software updates via System Preferences

Right now, this is a bit more of a proof-of-concept, but since /usr/sbin/softwareupdate has become increasingly unreliable in the past year for automating and enforcing Apple software updates, having a way to automate installing updates through the GUI on certain relatively unattended Macs is worth exploring. I’ve created a GitHub project for that called Sys Pref Software Updates (or spsu, for short).

Mac admin'ing

Fixing Jamf device signature error

Even though this Jamf Nation thread is five years old, as of this writing, it’s still got the solution to the Device Signature Error - A valid device signature is required to perform the action error message.

In my experience, the actual working solution is to run sudo jamf enroll -prompt and then enter credentials when prompted. Repeatedly running sudo jamf recon (even after a reboot) or sudo jamf policy doesn’t fix the issue, nor does verifying that the system clock time is correct.

Now why this comes up in the first place on a freshly factory-reset computer that DEP-enrolled in Jamf—who knows but Jamf?

Mac admin'ing

Running commands as a user when scripting for Munki or Jamf

Munki and Jamf run as root, so scripts they execute execute as root, not user.

One great way around this is to use Outset‘s login scripts (login-once, login-every), but sometimes you may have occasion to actually run a script immediately as the logged-in user.

Obviously, you’ll want to get the currently logged-in user into a variable you can use—several methods for that are described in How To: Get the currently logged in user, in a more Apple approved way—and you’ll want to watch out for the “logged in user” being _mbsetupuser, root, or just blank.

Then, you can use su to substitute a user identity:
/usr/bin/su -l "$loggedInUser" -c "commandyouwanttorunastheuser"

This is kind of a hack, so whether you’re using this as a postinstall_script in a Munki nopkg or a script that a Jamf policy is calling, you’ll definitely want to thoroughly test it to make sure it does what you want it to do

Mac admin'ing

Fix for custom user icons freezing up System Preferences

Even though there is some flexibility in terms of what sizes and resolutions you can use for custom user icons (to select for user pictures), if your icon’s resolution is way off, you may see a frozen blank, grey screen when trying to change the picture from that too-high-res picture to something else:

To get out of that, you’ll have to force-quit System Preferences, and then delete the picture manually with the terminal:
sudo dscl . delete /Users/username JPEGPhoto

The fix to prevent this from happening again is rather simple. Just make sure your image matches the size and pixels-per-inch that the macOS system user icons have:

Then, you should be able to select another image after your image is selected.

Mac admin'ing

Introduction to Nudge

What is Nudge?

In 2018, Erik Gomez created Nudge, which is open source and has nine other contributors as of this writing.

Part of the beauty of Nudge is its simplicity—it doesn’t actually install any updates itself (it does invoke the softwareupdate binary to check for or download Apple software updates, but it doesn’t actually install anything). That means if Apple changes how it does updates so as to make it particularly difficult for tools like Munki (yay!) or Jamf (eh…) to manage updates, you can just… nudge… users to install their updates via System Preferences.

How does it work?

There is a launch agent that checks to see if your major OS is up to date (e.g., 10.15) or if your minor OS is up to date (e.g., 10.15.2 build 19C57). It will pop up a window if the OS isn’t “up to date” (however you define it), and get more aggressive as the cut-off date (something you also define) gets closer (or has passed).

You build it with munkipkg, though I’d recommend distributing separately from the package both the nudge.json (renamed from example_config.json) and the update_ss.png image. The former you can even host online (not on the user’s machine), but both you may want to update from time to time, even if the rest of the Nudge package (including scripts, launch agent, your company logo) stay the same.

The Nudge script will check to see if the major OS and/or minor OS/build are/is up to date, and, if not, nudge the user to install the updates.

What does this look like to the user

Every half hour, the user will get a window that appears over whatever she is working on, and the window isn’t moveable and can’t be closed with a red X button.

Initially, there will be a Close button readily available.

With the default timer settings, the window will come back to the front once every 4 hours.

Once it’s within three days of the cut-off date, the user will have to click I understand in order to get the Close button to appear.

Then, the user is okay to click the Close button. Of course, your hope is that the user will just click Update Machine instead to actually get the machine to update instead of just dismissing the dialogue.

With the default timer settings, the window will come back to the front once every 2 hours.

The same click-I-understand-before-you-click-close-but-really-please-just-update-your-machine routine shows up, too, when there is only one day left.

With the default timer settings, the window will come back to the front once every 10 minutes.

If the cut-off date is within an hour or if it’s passed already, there is no option to close or say you understand. You can’t dismiss the window.

With the default timer settings, within an hour of the cut-off date, the window will be brought back to the foreground every 1 minute.

And with the default timer settings, when the cut-off date has passed, the window will be brought back to the foreground every 10 seconds.

What can you configure via the .json file?

I’m a huge fan of Munki, but one downside to using Munki to install Apple software updates is you can’t really add a preupgrade_alert for a non-optional item, which means you can’t really alert users that, say, a particular Apple software update may have the screen turn black for a full minute.

With Nudge, you can configure pretty much any of the text using the .json file.

You can also have the Update Machine button go wherever you’d like it to go. You can even have it invoke Munki or Jamf.

If you don’t want it to invoke up Munki or Jamf, or to launch up a pre-downloaded update or upgrade installer, you can also have this in your .json file:
"path_to_app": "/System/Library/PreferencePanes/SoftwareUpdate.prefPane",
so it just launches up System Preferences > Software Update, so the user can install updates via the “Apple sanctioned” way.

If you don’t want to bother the user every half hour, you can set the days_between_notifications preference. This goes by actual day and not necessarily a full 24 hours. For example, if the user was notified Thursday afternoon and dismissed the Nudge notification, she may see the notification again on Friday morning (since Friday is the next day after Thursday).

You can also adjust the timer settings to be more or less aggressive with how quickly the open window will be brought back to the foreground.

What else is there about Nudge?

This blog post isn’t intended to be a comprehensive Nudge handbook—just an introduction to Nudge.

You can (and should), of course, read the Nudge README.

I’d also highly recommend just reading the actual Nudge code, since it gives you a sense of the sequence of things and how it all works (especially if you read the comments in the code).

Finally, check out the #nudge channel on the MacAdmins Slack if you have any questions about how to use Nudge.