Tag Archive

Munki can get into a notification loop if root is using the Persian calendar

Posted on December 13, 2020 by alanysiu

The Problem

At some point, this blog post may be obsolete, because I’m hoping this will be fixed soon, but in the meantime, I’ve filed an issue on GitHub: Using Persian calendar results in notification loop at every next Munki run

If you have users who claim to see notifications multiple times a day, even if you have DaysBetweenNotifications set to 1 or more, you may want to have them check what calendar/locale root is using.

When you select a locale, there is a default calendar that goes along with it. To see this in action, try going to System Preferences Language & Region > General. If you set the region to Asia > Afghanistan, you’ll see the calendar automatically change to Persian, and then if you set the region to Americas > United States, you’ll see the calendar automatically change to Gregorian.

At that point, if you run defaults read ~/Library/Preferences/.GlobalPreferences.plist AppleLocale, you should see en_US as a result. But if you keep the locale as United States and manually switch the calendar type to Persian, then defaults read ~/Library/Preferences/.GlobalPreferences.plist AppleLocale should return en_US@calendar=persian.

That’s just for your user account, though. Munki doesn’t care about the locale or calendar set for the user account, but if the calendar (or calendar set by the default locale) is Persian for the root account, Munki (as of this writing, but hopefully not much sooner afterwards) will get into a notification loop, and your users will either see this every hour or so:

Or, worse yet, after three days, see Managed Software Center pop up every hour or so:

A Workaround

As a workaround, you may want to verify that your users have a locale specified in /Users/username/Library/Preferences/.GlobalPreferences.plist, and then change the locale in /Library/Preferences/.GlobalPreferences.plist to one that uses Gregorian by default.

Alternatively, you can keep it at the old locale but just specify the calendar; for example: sudo defaults write /Library/Preferences/.GlobalPreferences AppleLocale "en_AF@calendar=gregorian"

Automated Workaround

Since I don’t know how long it will take to fix this issue (it may not be an easy fix), I created a nopkg that “fixes” this issue by changing the root-used locale/calendar while leaving the user-specific ones in place.


User preference should already exist

Based on my limited testing (Catalina 10.15.7 only), it appears if you create a fresh user account, that user account automatically gets its own ~/Library/Preferences/.GlobalPreferences.plist file with the AppleLocale prepopulated based on what’s in /Library/Preferences/.GlobalPreferences.plist, but it lives independently. So, in theory, if you have a single-user machine in which the user account has already been created, you might be safe in just writing directly to /Library/Preferences/.GlobalPreferences.plist without messing up the user’s ~/Library/Preferences/.GlobalPreferences.plist.

Adjusting root’s user preference doesn’t seem to make a difference

sudo defaults write /private/var/root/Library/Preferences/.GlobalPreferences AppleLocale "en_US@calendar=gregorian" doesn’t seem to override what’s in defaults read /Library/Preferences/.GlobalPreferences AppleLocale.


Fix for VirtualBox Extension Pack postinstall script hanging in Munki

Posted on September 30, 2020 by alanysiu

The problem

If you’ve been running the VirtualBoxExtPack.munki.recipe AutoPkg recipe, and you’ve noticed the VirtualBox Extension Pack postinstall script in Munki hanging indefinitely (30 minutes and beyond), it’s because the license hash has changed.

The fix

According to @jessepeterson (the maintainer of that AutoPkg recipe), the license hash doesn’t change very often, but it did change, and @lctrkid made a pull request (now merged in) on that recipe repo to put in the new license hash, so you may have to run a autopkg repo-update jessepeterson-recipes and then possibly re-create the recipe override or manually add the license part of the override.

Some background

The license hash is something you can find by manually running a command like this (with the actual extension pack in your Downloads folder, of course): sudo /usr/local/bin/VBoxManage extpack install --replace ~/Downloads/Oracle_VM_VirtualBox_Extension_Pack-6.1.14.vbox-extpack. It will display the whole license, and then prompt you to accept the terms: Do you agree to these license terms and conditions (y/n)? y

Once you’ve accepted, you will then see the hash:

License accepted. For batch installation add
to the VBoxManage command line.

Successfully installed "Oracle VM VirtualBox Extension Pack".


Special thanks to @lctrkid and @jessepeterson on the MacAdmins Slack for helping understand what’s going on.


How to deploy a .pkg via Munki if a config file has to be in the same directory

Posted on July 22, 2020 by alanysiu

Vendors package software in funny ways sometimes. Every now and then, you might come across a vendor .pkg that comes with some kind of .xml or .cfg or .txt that has to be in the same directory as the .pkg. It’s likely because there’s some postinstall script in the .pkg itself that references that text file via relative path.

There are basically two approaches you can take here with Munki.

Approach #1 would be to create another .pkg that delivers that .pkg as a payload to a directory of your choosing (e.g., /tmp) and also delivers the config file to that same directory. Then, in your custom .pkg, you have your own postinstall script that runs something like installer -pkg /tmp/nameofpackages.pkg -target /

Approach #2 is what I’d recommend, if you’re using Munki, which would be manually creating a disk image that has both the .pkg and config file in the same directory, and then importing that disk image into Munki. Munki mounts .dmgs to an arbitrary random mount point, but since the .pkg and config file will be in the same directory within the mounted .dmg, it won’t matter, and when Munki sees the .pkg inside the .dmg, Munki will just install the .pkg, and everything will be cool.


Some basics of DEPNotify and a sample script

Posted on July 15, 2020 by alanysiu

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 (DEPNotifyMunkiSample.sh) 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:


Python 3 script to add optional installs to the SelfServeManifest

Posted on June 24, 2020 by alanysiu

Two years ago, I wrote a bash script that adds a bunch of optional installs to the SelfServeManifest using /usr/libexec/PlistBuddy, which is a fine tool, but it can get bit messy sometimes. I did play around with using /usr/local/munki/manifestutil, but it got a bit convoluted, and I figured “Hey, why not just write it in Python 3, now that the default shell is zsh instead of bash and Python 2 is end-of-life?”

So, yeah, the rationale here is that you may want to have a bunch of applications installed as default applications for your users but still give users the option to remove those applications later, so this Python 3 script would just make it seem to Munki as if the user has already selected these optional installs to install… and then she can always use Managed Software Center to remove those optional installs later if she doesn’t want them any more.


Script to make Jamf Self Service policy install a Munki optional install

Posted on June 10, 2020 by alanysiu

There is a huge project called jamJAR that seeks to integrate Munki and Jamf in a seamless way.

I’ve written a script that does something a bit less ambitious, but it may still be helpful to your organization if you are “using” Jamf Self-Service, really want to be using Managed Software Center, and still want to keep Self-Service around (either temporarily or semi-permanently).

Basically install_munki_optional.py is a script that you can include in a Jamf Self-Service policy that will attempt to install a Munki optional install item. Just make the item name (not necessarily the same as the display name, mind you) the Parameter 4 in Parameter Values. Oh, and make sure that item actually is an optional install available in Managed Software Center!

The script will attempt to add that item to the SelfServeManifest. If the item already exists in the SelfServeManifest, the script will launch up Managed Software Center and try to focus on the item to basically say “Hey, remember this is already installed?” If the item doesn’t already exist in the SelfServeManifest, the script will add the item to the SelfServeManifest, launch up Managed Software Center, and attempt a managedsoftwareupdate --auto run.

As it’s written, it’s using Munki’s embedded Python 3, but if you have your own Python 3 you’d rather use, just update the shebang path.


Using a full macOS installer with Munki to patch macOS

Posted on June 3, 2020 by alanysiu

Note about Silicon Macs

This will not work with Apple Silicon Macs, as Apple now requires you to enter the password of a secure token user account in order to run startosinstall.


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 Catalina.app 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 Catalina.app</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\ Catalina.app/Contents/Resources/startosinstall --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) >= 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.


Force-stopping the MunkiStatus progress bar at the login window

Posted on April 16, 2020 by alanysiu

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.


Using a Munki nopkg to disable Chrome 80’s ScrollToTextFragment feature

Posted on March 4, 2020 by alanysiu

Update, 14 July 2020

With Chrome 84, Google has now removed the ability to disable the ScrollToTextFragment feature, so this whole write-up is now obsolete

What is ScrollToTextFragment

With Chrome 80, Google has introduced a new ScrollToTextFragment feature that allows you to reference an anchor link by any phrase that’s in a webpage, even if the author of the page hasn’t created an anchor link.

You can see this setting by going to chrome://flags in your Chrome browser (assuming you’re using version 80+).

By default, Chrome 80 has the setting enabled.

And, for example, you can create a direct link to the Recent Posts part of my blog by just appending #:~:text=Recent%20Posts to the end of the website URL.

If you disable this setting, however, the regular old webpage behavior returns.

So visiting the URL with that appended part will do nothing but show you the top of the webpage.

Why might you want to disable ScrollToTextFragment?

David Bokan (from Chromium, the open source project Chrome is based on) wrote up a Google Doc called Scroll-to-text Fragment Navigation – Security Issues (PUBLIC) that explains some potential issues.

How can I automate disabling ScrollToTextFragment?

Well, as of this writing (March, 2020), there doesn’t appear to be a way you can disable this via policy, so the best way I’ve come across to do so is via script. It’s a bit convoluted, but it works—here’s my Munki nopkg for disabling ScrollToTextFragment.

Because changing that setting requires a relaunch of Chrome, the nopkg has GoogleChrome as a blocking application, which means if you want to enforce this, you may have to add a force_install_after_date key to the pkginfo, because the chances that your users will see a pending Managed Software Center update, quit of Chrome, install the update, and then launch up Chrome again are probably fairly low.