Manage the Look of Launchpad

You can control the number of columns and rows in LaunchPad. To do so, edit the defaults domain with the key springboard-rows for the number of rows to display and springboard-columns to control the number of columns displayed. So to set the number of rows LaunchPad will show per screen, send the write verb into defaults for along with the springboard-rows and an -int of 4:

defaults write springboard-rows -int 4

Likewise, to set columns to 8:

defaults write springboard-columns -int 8

Then just killall for Dock:

killall Dock

In some cases you will also need to send a resetlaunchpad boolean into (for TRUE) along with a killall for Dock (or reboot):

defaults write resetlaunchpad -bool TRUE; killall Dock

Limit Upload and Download Streams for Google Drive File Stream on macOS

Google Drive File Stream allows you to access files from Google’s cloud. It’s pretty easy for a lot of our coworkers to saturate our pipes. So you can configure a maximum download and upload speed in kilobytes per second. To do so write a defaults domain into /Library/Preferences/ and use a key of BandwidthRxKBPS for download and BandwidthTxKBPS for upload (downstream and upstream as they refer to them) as follows:

defaults write BandwidthRxKBPS -int 200
defaults write BandwidthTxKBPS -int 200

Updated My Apple Admin Conferences Page

I’ve been keeping a list of Apple Admin conferences for a few years now. I probably should have versioned it and kept each iteration, but… no need to pollute the interwebs with more outdated stuffs than I already have. So here’s the link for the latest version, updated with all the event dates announced thus far:

Hope to see you at some!

Inspecting and creating Mac installer packages on Linux

Awhile back, I wrote a tool to rewrap ipa files that I called ipasign: But I wanted to do something similar for the Mac, and specifically have it run in Linux. So looking at what you’d need to be able to do, let’s start with viewing the contents of a flattened Apple package. This command will show you the files installed as a part of the Node JS package. Why did I choose that package? It was sitting on my desktop…

pkgutil --files org.nodejs.node.pkg

Now, this logic is available because you’re running pkgutil on a Mac. But that can’t run in Linux. So what would you do if you wanted to complete that same operation? If the package hasn’t been flattened then you can simply traverse the files in the package. If it has been flattened (and it must be in order to properly be signed) then that can’t work. So to see the files installed from a Linux system will require a tad bit more work. First, we’ll create a directly to extract our package into:

mkdir node-v8.11.1.pkg

Then cd into that directory and use xar to extract the package:

xar -xf /Users/charles.edge/Downloads/node-v8.11.1.pkg

In there, you’ll see three files: Bom, PackageInfo, and Payload. The contents, which mimic the –files option to some extent are found by first changing the name of payload to Payload.gz:

mv ./node-v8.11.1.pkg/Payload ./node-v8.11.1.pkg/Payload.gz

Then unzipping it:

gunzip Payload

And viewing the contents:

cpio -iv < Payload

Or throw all that into a one-liner:

cpio -o | gzip -c > Payload

You can also use bomutils to traverse and make BOMs:

You can also see some metadata about how the package will lay down by catting the distribution file:

<?xml version=”1.0″ encoding=”utf-8″ standalone=”yes”?>
<installer-gui-script minSpecVersion=”1″>
<welcome file=”welcome.html”/>
<conclusion file=”conclusion.html”/>
<background alignment=”topleft” file=”osx_installer_logo.png”/>
<pkg-ref id=”org.nodejs.node.pkg” auth=”root”>
<pkg-ref id=”org.nodejs.npm.pkg” auth=”root”>
<options customize=”allow” require-scripts=”false”/>
<license file=”license.rtf”/>
<line choice=”org.nodejs.node.pkg”/>
<line choice=”org.nodejs.npm.pkg”/>
<choice id=”org.nodejs.node.pkg” visible=”true” title=”Node.js v8.11.1″>
<pkg-ref id=”org.nodejs.node.pkg”/>
<pkg-ref id=”org.nodejs.node.pkg” version=”v8.11.1″ onConclusion=”none” installKBytes=”37377″>#node-v8.11.1.pkg</pkg-ref>
<choice id=”org.nodejs.npm.pkg” visible=”true” title=”npm v5.6.0″>
<pkg-ref id=”org.nodejs.npm.pkg”/>
<pkg-ref id=”org.nodejs.npm.pkg” version=”v5.6.0″ onConclusion=”none” installKBytes=”20113″>#npm-v5.6.0.pkg</pkg-ref>

If you want to make a package, check out this gist:

Next up, you frequently want to check the signature of a package. So to see the signature, I can simply use: pkgutil if on a Mac:

pkgutil --check-signature org.nodejs.node.pkg

Or I can use codesign:

codesign -v node-v8.11.1.pkg

The beauty of codesign is that it’s been open sourced by Apple. The bummer about codesign is that it uses multiple CoreFoundation frameworks:

otool -L /usr/bin/codesign


/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation (compatibility version 150.0.0, current version 1452.23.0)

/System/Library/Frameworks/Security.framework/Versions/A/Security (compatibility version 1.0.0, current version 58286.51.6)

/usr/lib/libc++.1.dylib (compatibility version 1.0.0, current version 400.9.0)

/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1252.50.4)

New Page Explaining Apple MDM Even Further

Apple has published a new page that goes through all of the settings and commands available via MDM and explains many in much more detail. This is available at The new guide is a great addition to the work @Mosen has done at in terms of explaining what each setting, command, and payload do. And let’s not forget the definitive MDM protocol reference guide, available at

Overall, I’m excited to see so much information now available about MDM, including how to develop an MDM properly, what each setting does, and now what you should expect out of an MDM!

Getting Started with Autopkgr

Autopkgr is basically a small app that allows you to select some repositories of recipes and then watch and run them when they update. It’s a 5 minute or less installation, and at its simplest will put software packages into a folder of your choosing so you can test/upload/scope to users. Or you can integrate it with 3rd party tools like Munki, FileWave, or Jamf using the JSSImporter. Then if you exceed what it can do you can also dig under the hood and use Autopkg itself. It’s an app, and so it needs to run on a Mac. Preferably one that doesn’t do much else. 

Installing Autopkgr

You can obtain the latest release of Autopkgr at To install, drag the app to the Applications folder. 

When you open AutoPkgr for the first time, you’ll prompted for the user name and password to install the helper tool (think menu item). 

The menu item then looks like the following.

These are the most common tasks that administrators would run routinely. They involve checking Autopkg recipes to see if there are new versions of supported software titles, primarily. Opening the Autopkgr app once installed, though, shows us much more. Let’s go through this screen-by-screen in the following sections.

Moving AutoPkg Folders Around 

By default, when installed with Autopkgr, Autopkg stores its cache in ~/Library/AutoPkg/Cache and the repos are sync’d to ~/Library/AutoPkg/RecipeRepos. You can move these using the Choose… button in the Folders & Integration tab of Autopkgr, although it’s not necessary (unless, for example, you need to move the folders to another volume). 

Note: You can also click on the Configure AutoPkg button to add proxies, pre/post processing scripts, and GitHub tokens if needed. 

Keeping Autopkg and Git up-to-date

The Install tab is used to configure AutoPkg settings. If there is a new version of AutoPkg and Git, you’ll see an Install button for each (used to obtain the latest and greatest scripts); otherwise you’ll see a green button indicating it’s up-to-date. 

You can also configure AutoPkgr to be in your startup items by choosing to have it be available at login, and show/hide the Autopkgr menu item and Dock item. 

Configuring Repositories and Recipes

Repositories are where collections of recipes live. Recipes are how they’re built. Think of a recipe as a script that checks for a software update and then follows a known-good way of building that package. Recipes can then be shared (via GitHub) and consumed en masse. 

To configure a repository, click on the “Repos & Recipes” tab in Autopkgr. Then select the repos to use (they are sorted by stars so the most popular appear first). 

Note: There are specific recipes for Jamf Pro at

Then you’ll see a list of the recipes (which again, will make packages) that AutoPkgr has access to. Check the ones you want to build and click on the Run Recipes Now. 

If you don’t see a recipe for a title you need, use the search box at the bottom of the screen. That would show you a given entry for any repos that you’ve added. Again, all of the sharing of these repos typically happens through GitHub, but you can add any git url (e.g. if you wanted a repo of recipes in your organization. 

Once you’ve checked the boxes for all the recipes you want to automate, you can then use the “Run AutoPkg Now” option in the menu items to build, or rely on a routine run, as described in the next section.

Scheduling Routine Builds

Autopkgr can schedule a routine run to check recipes. This is often done at night after administrators leave the office. To configure, click on the schedule tab and then check the box for Enable scheduled AutoPkg runs. You can also choose to update your recipes from the repos by checking the “Update all recipes before each AutoPkg run” checkbox.

Getting Notified About New Updates To Packages

I know this sounds crazy. But people like to get notified when there’s a new thing showing up. To configure a variety of notification mechanisms, click on the Notifications tab in AutoPkgr.

Here, you can configure alerts via email, Slack, HipChat, macOS Notification Center, or via custom webhooks.

Integrating Autopkg with Jamf (and other supported vendors)

When integrating with another tool, you’ll need to first install the integration. To configure the JSSImporter, we’ll open the “Folders & Integrations” tab in Autopkgr and then click on the Install JSSImporter button.

Once installed, configure the URL, username and password (for Customer API access) and configure any distribution points that need to have the resultant packages copied to. 

Once the JSSImporter is configured, software should show up in Jamf Pro scoped to a new group upon each build.  It is then up to the Jamf Administrator to complete the scoping tasks so software shows up on end user devices.

What the JSSImporter Does from Autopkg

This option doesn’t seem to work at this time. Using the following may make it work:

sudo easy_install pip && pip install -I --user pyopenssl

Note: The above command may error if you’re using macOS Server. If so, call easy_install directly via 


Stop Apps From Installing Automatically On A Mac When Purchased On Another Mac

You know how when you buy apps at home, they show up on your computer at work, if you’re using the same iCloud account for the app store in both locations? Some companies want to disable that. To do so, send a ConfigDataInstall key into, which can most easily be done with the defaults command:

sudo defaults write ConfigDataInstall -int 0

Or to turn it back on:

sudo defaults write ConfigDataInstall -int 1

Using sysadminctl on macOS

macOS 10.13 brings changes to sysadminctl. You know those dscl scripts we used to use to create users? No longer supposed to be necessary (luckily they do still work). Now you can create a user with a one-liner, and do other forms of user management, such as enabling FileVault for a given user, or managing the guest accounts. However, you can’t do these tasks as root or via sudo. You have to do so with other admin accounts per Apple kbase HT208171 (in fact, this article has been in my queue waiting for that issue to be fixed – but keep in mind I’m not prefacing these with sudo in the below commands). In the below command, we’ll pass the -addUser option and then use -fullName to fill in the displayed name of the user, -password to send a password to the account and -hint so we can get a password hint into that attribute:

sysadminctl -addUser krypted2 -fullName "Charles Edge" -password testinguser -hint hi

The result would be as follows:

No clear text password or interactive option was specified (adduser, change/reset password will not allow user to use FDE) !
Creating user record…
Assigning UID: 503
Creating home directory at /Users/krypted2

Notice that in the above, the system automatically selected a home directory and UID. We could have passed those as well, using Now let’s use dscl to view the user we just created:

dscl . -read /Users/krypted2

Here’s a snippet of the dscl output:

NFSHomeDirectory: /Users/krypted2
Password: ********
Picture: /Library/User Pictures/Fun/Ying-Yang.png
PrimaryGroupID: 20
RealName: Charles Edge
RecordName: krypted2
RecordType: dsRecTypeStandard:Users
UniqueID: 503
UserShell: /bin/bash

Notice that the above is not the whole record you’d typically find with dscl. But if it were, you would not have the AuthenticationAuthority attribute. To see if it can unlock FileVault we can use the -secureTokenStatus operator built into sysadminctl. Simply pass the RecordName and you’ll get an indication if it’s on or off:

sysadminctl -secureTokenStatus krypted2

The response should be as follows:

Secure token is ENABLED for user Charles Edge

To just get the ENABLED response we’ll just use awk to grab that position (also note that we have to redirect stderr to stdout):

sysadminctl -secureTokenStatus charles.edge 2>&1 | awk '{print$7}'

We could append the AuthenticationAuthority attribute with dscl, as we would need a SecureToken. To get a SecureToken, we’ll use the -secureTokenOn verb:

sysadminctl -secureTokenOn krypted mysupersecretpassword

To disable, we’ll use -secureTokenOff

sysadminctl -secureTokenOff krypted mysupersecretpassword

Given that we like to rotate management passwords, we can do so using-resetPasswordFor which takes a username and a password as -newPassword and -passwordHint respectively:

sysadminctl -resetPasswordFor krypted -newPassword newsupersecretpassword -passwordHint "That was then this is now"

Note: In the above, we quoted the hint, which is supplied using the -passwordHint option. If it was one word we wouldn’t have needed to do so. 

Next, let’s check guest access. You can have guest enabled for logging in, afp, or smb. To check if guest is enabled for one of these use the -guestAccount, -afpGuestAccess, or -smbGuestAccess options. Each has an on, off, and status verb that can be used to manage that account type. So for example, if you wanted to check the status of the guest account, you could use -guestAccount as follows (also note that we have to redirect stderr to stdout):

sysadminctl -guestAccount status 2>&1 | awk '{print$5}'

To then disable if it isn’t already disabled:

sysadminctl -guestAccount Off

You can also use sysadminctl to do a quick check of the encryption state of the boot volume using the -filesystem option (although there’s no on and off verb for this option just yet):

bash-3.2# sysadminctl -filesystem status

2017-12-07 10:37:26.401 sysadminctl[8534:466661] Boot volume CS FDE: NO

2017-12-07 10:37:26.434 sysadminctl[8534:466661] Boot volume APFS FDE: YES

The help page is as follows:

Usage: sysadminctl [[interactive] || [-adminUser -adminPassword ]] -deleteUser [-secure || -keepHome] -newPassword -oldPassword [-passwordHint ] -resetPasswordFor -newPassword [-passwordHint ] -addUser [-fullName ] [-UID ] [-shell ] [-password ] [-hint ] [-home ] [-admin] [-picture ] -secureTokenStatus -secureTokenOn -password -secureTokenOff -password -guestAccount -afpGuestAccess -smbGuestAccess -automaticTime -filesystem status Pass '-' instead of password in commands above to request prompt.

Why should you switch to sysadminctl for scripts? Entitlements and I’m sure this is how mdmclient will pass management commands in the future… Why should you not? You can’t run most of it as root…

Check the EFI Version of a Mac

I’d written an efi version checker. But the lovely Andrew Seago texted me one that’s better than mine. So I present it here: current_efi_version=`/usr/libexec/efiupdater | grep "Raw" | cut -d ':' -f2 | sed 's/ //'`
echo "current_efi_version $current_efi_version"
latest_efi_version=`ls -La /usr/libexec/firmwarecheckers/eficheck/EFIAllowListShipping.bundle/allowlists/ | grep "$current_efi_version"`
echo "latest_efi_version $latest_efi_version"
if [ "$latest_efi_version" == "" ]; then
exit 1
exit 0