Quantcast
Channel: rtrouton – Der Flounder
Viewing all 764 articles
Browse latest View live

Session videos from Jamf Nation User Conference 2022 now available


Creating a NexThink installer for deployment via Jamf Pro

$
0
0

A while back, I had to build an installer for NexThink Collector which could be deployed via Jamf Pro. NexThink can be interesting to deploy because the installation process:

  1. Involves an application named csi.app, which has a command line tool.
  2. The referenced csi app’s command line tool configures and runs an installer package.
  3. The command line tool also needs to reference a license file, which NexThink refers to as a CustomerKey file.

The CustomerKey file should look similar to what’s shown below:


This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters


—–BEGIN CUSTOMER KEY—–MIIDhzCCAm+gAwIBAgIEIa+KoTANBgkqhkiG9w0BAQsFADBbMScwJQYDVQQDDB5SZWdlcnkgU2VsZi1TaWduZWQgQ2VydGlmaWNhdGUxIzAhBgNVBAoMGlJlZ2VyeSwgaHR0cHM6Ly9yZWdlcnkuY29tMQswCQYDVQQGEwJVQTAgFw0yMjEyMDIwMDAwMDBaGA8yMTIyMTIwMjIwMDIxMFowSTEVMBMGA1UEAwwMbG9jYWxob3N0LmlvMSMwIQYDVQQKDBpSZWdlcnksIGh0dHBzOi8vcmVnZXJ5LmNvbTELMAkGA1UEBhMCVUEwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDaKRW9KeX4wg/838FkxmzaBjqf1DeKD5GKEqhUKz0y78Wwnsv2zAXGM4UkdZJP9zHtC9/wFQT+lhclDlogxkU9lfMADV7nMdGL0GkJzwMQNS52dPNXDup7/d9yRkyjkV0Pf4t2fJF3igoNXFQuBvuArkNV6hfja2gOEczOSAaJ7L7qRnSahLjciJRaCuEPjwneh3krhOFT+djwuYJMIvBDEqs+gfp4OPDDBtVg2scUUGRmHsC+JAoK+JwqYwB9TNt+9hZtGfDqgZSHebXEfRTguhQpBj0mPTo76EahAbHbXJhV+efg3jt32pZ6qRl8ffrZAjefWEAnOMyXQ7fbL+bpAgMBAAGjYzBhMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBRNHRZG3IKNH0kTRaiVfq6N8Ovp5zAfBgNVHSMEGDAWgBRNHRZG3IKNH0kTRaiVfq6N8Ovp5zANBgkqhkiG9w0BAQsFAAOCAQEAhpbntg+nwhIKgRuUidu/wXn197Ah0Pd4CYYxG5dR9rg8nWObx4QO6ApIH91nUUQVuV6mSTFtfy4yNQzxaROgZP9hDNvhd78D/ewXxp6bN/Xkn+c7SWrs/b1vHb2Dr1sDP4F9SAOrCI6TdoYa8UNhPXXSTt8M/hGSB2oWOpT2FAb2IbdmdYhDaibcJwp+/Had1FLbeDZgdgYCFoZLjws/9E/pIXjSxBYAJLbaQZffrfO5jCe2KesE73iQatW2IPynsFifRGGoMHXVLOfsLA9c2KDGqDmnJ+PvsBSe9rIpSJYC4WjR5Mt8W88kQSj05b9NqCsXmmMDEbD8uVLyKvQihA==—–END CUSTOMER KEY—–

All the needed components with the exception of the CustomerKey file, which is different for each customer, ship on a disk image.

Screenshot 2022 12 02 at 3 31 46 PM

NexThink’s install documentation for the macOS version of the Collector software assumes that a human is doing one of the following:

Graphical installation: Mounting the disk image, double-clicking on the installer package and following the prompts, entering the correct configuration information were needed.

Screenshot 2022 12 02 at 3 46 29 PM

 

Screenshot 2022 12 02 at 3 46 39 PM

Command line installation: Mounting the disk image, opening the Terminal application and using the csi app’s command line tool to configure the installer package and run the installation process.

Screenshot 2022 12 02 at 3 46 53 PM

 

Screenshot 2022 12 02 at 3 47 02 PM

 

For the Enterprise Deployment section of the application, the NexThink documentation says they support it but doesn’t provide information on how to do it.

Screenshot 2022 12 02 at 3 47 10 PM

In my case, I decided to do the following to deploy it via Jamf Pro:

  1. Wrap the disk image and CustomerKey file inside a separate installer package.
  2. Use a postinstall script to perform the following actions:

A. Identify the location of the disk image stored inside the installer package.
B. Mount the disk image
C. Identify the location of the csi.app on the mounted disk image.
D. Identify the location of the CustomerKey file stored inside the installer package.
E. Use the csi app’s command line tool to configure and run the NexThink-provided installer package on the mounted disk image, to install the NexThink Collector software.
F. Unmount the disk image.

For more details, please see below the jump.

Note: The details of installing and configuring NexThink are going to vary between shops, because different shops are going to configure different options for NexThink. Please consider what’s shown below as a general example, not something that will work for all environments.

Pre-requisites:

  • Packages
  • Vendor-provided NexThink disk image with the NexThink Collector installer for macOS
  • Vendor-provided CustomerKey text file

Before building the package, you’ll need to create a directory named CustomerKeys somewhere convenient.

Screenshot 2022 12 02 at 4 09 34 PM

Once the CustomerKeys directory has been created, add the CustomerKey file to it. The CustomerKey file is a plaintext file, where the filename must end in the .txt file extension. For this example, the CustomerKey file is named Company-Name-customer-key.txt.

Screenshot 2022 12 02 at 4 09 23 PM

 

Building the NexThink Collector installer

1. Set up a new Packages project and select Raw Package.

Screenshot 2022 12 02 at 3 59 32 PM

2. In this case, I’m naming the project NexThink Collector Install 22.9.1.14.

Screenshot 2022 12 02 at 4 00 21 PM

 

3. Once the Packages project opens, click on the Project tab. You’ll want to make sure that the your information is correctly set here (if you don’t know what to put in, check the Help menu for the Packages User Guide. The information you need is in Chapter 4 – Configuring a project.)

Screenshot 2022 12 02 at 4 01 46 PM

In this example, I’m not changing any of the options from what is set by default.

 

4. Next, click on the Settings tab. In the case of my project, I want to install with root privileges and not require a logout, restart or shutdown.

To accomplish this, I’m choosing the following options in the Settings section:

In the Tag section:

  • Identifier: set as appropriate (for my installer, I’m using com.nexthink.pkg.collector)
  • Version: set as appropriate (for my installer, I’m using 22.9.1.14 )

In the Post-installation Behavior section:

On Success: should be set to Do Nothing.

In the Options section:

  • Require admin password for installation should be checked
  • Relocatable should be unchecked
  • Overwrite directory permissions should be unchecked
  • Follow symbolic links should be unchecked

Screenshot 2022 12 02 at 4 02 39 PM

 

5. Select the Payload tab. Nothing here should be changed from the defaults.

Screenshot 2022 12 02 at 4 03 01 PM

 

6. Select the Scripts tab.

Under the Additional Resources section, add the following file and directory:

The NexThink disk image

Screenshot 2022 12 02 at 7 54 53 PM

Screenshot 2022 12 03 at 11 30 32 AM

The CustomerKeys directory containing the CustomerKey file.

Screenshot 2022 12 02 at 7 55 10 PM

Screenshot 2022 12 03 at 11 30 08 AM

The last part is telling the NexThink installer to run, using the csi app’s command line tool. For this, you’ll need a postinstall script.

Here’s the postinstall script being used for this example installer package:


This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters


#!/bin/bash
# Description: Script to install the NexThink Collector agent using the csi.app's command-line
# options to install and register the NextThink agent using your organization's Customer Key.
ERROR=0
# File Paths
if [[ -f "$(/usr/bin/find $(dirname $0) -maxdepth 1 \( -iname \*\.dmg \))" ]]; then
dmgFile="$(/usr/bin/find $(dirname $0) -maxdepth 1 \( -iname \*\.dmg \))"
fi
dmgMount="$(/usr/bin/mktemp -d /tmp/NexThink_Collector.XXXX)"
csiAppPath="$dmgMount/csi.app/Contents/MacOS/"
# Remove the trailing slash from the csiAppPath variable if needed.
csiAppPath=${csiAppPath%%/}
#installer variables
udpport="999"
tcpport="443"
tcpdata="enable"
assignment="enable"
tag_value="0"
nexthink_address="nexthink.server.goes.here"
customerKey="$(dirname $0)/CustomerKeys/*.txt"
# Mount the DMG
/usr/bin/hdiutil attach "$dmgFile" -mountpoint "$dmgMount" -nobrowse -noverify -noautoopen
# Install the Nextthink Collector software
# Define the parameters for csi.app for installing the Collector from the command line interface
"$csiAppPath"/csi -address "$nexthink_address" -port "$udpport" -tcp_port "$tcpport" -key "$customerKey" -data_over_tcp "$tcpdata" -use_assignment "$assignment" -tag $"tag_value"
#Disable/Enable Coordinator Service
launchctl bootout system /Library/LaunchDaemons/com.nexthink.collector.nxtcoordinator.plist
launchctl bootstrap system /Library/LaunchDaemons/com.nexthink.collector.nxtcoordinator.plist
# Unmount the DMG
hdiutil detach $dmgMount -force
exit $ERROR
view raw

postinstall

hosted with ❤ by GitHub

If not already selected, select the postinstall script and add it to the project.

Screenshot 2022 12 03 at 11 32 56 AM

Screenshot 2022 12 03 at 11 33 04 AM

Note: The options shown in the postinstall script for configuring NexThink are not going to work for all shops, because different shops are going to configure different options for NexThink. Please consider what’s shown above as a general example, not something that will work for all environments.

For more details on the available configuration options, please see the Command-line installation section of the NexThink documentation available via the link below:

https://docs.nexthink.com/platform/latest/installing-collector-on-macos

 

7. Build the package. (If you don’t know to build, check the Help menu for the Packages User Guide. The information you need is in Chapter 3 – Creating a raw package project and Chapter 10 – Building a project.)

Testing the installer

Once the package has been built, test it by installing it on a test machine which has the following:

  • Does not have the NexThink Collector software installed

The end result should be that the NexThink Collector software installs onto the Mac and is registered with the NexThink server.

Creating a NexThink uninstaller for deployment via Jamf Pro

$
0
0

As a follow-up to my previous post on building an installer for NexThink Collector which could be deployed via Jamf Pro, I also needed to build an uninstaller for this software. Fortunately, NexThink ships an uninstaller script on the same disk image that it uses to ship its installer.

Screenshot 2022 12 03 at 3 31 46 PM

NexThink’s install documentation for the macOS version of the Collector software assumes that a human is doing the following to run the uninstall process:

A. Mounting the disk image
B. Opening the Terminal application
C. Using the uninstaller script to run the uninstallation process.

Screenshot 2022 12 03 at 3 47 10 PM

In my case, I decided to do the following to deploy the uninstaller via Jamf Pro:

  1. Wrap the disk image inside a separate installer package.
  2. Use a postinstall script to perform the following actions:

A. Identify the location of the disk image stored inside the installer package.
B. Mount the disk image
C. Use the uninstall script to uninstall the NexThink Collector software.
D. Unmount the disk image.

For more details, please see below the jump.

Pre-requisites:

  • Packages
  • Vendor-provided NexThink disk image with the NexThink Collector uninstaller script

 

Building the NexThink Collector uninstaller

1. Set up a new Packages project and select Raw Package.

Screenshot 2022 12 04 at 3 53 01 PM

 

2. In this case, I’m naming the project NexThink Collector Uninstaller 22.9.1.14.

Screenshot 2022 12 04 at 3 53 24 PM

 

3. Once the Packages project opens, click on the Project tab. You’ll want to make sure that the your information is correctly set here (if you don’t know what to put in, check the Help menu for the Packages User Guide. The information you need is in Chapter 4 – Configuring a project.)

Screenshot 2022 12 04 at 3 53 48 PM

In this example, I’m not changing any of the options from what is set by default.

 

4. Next, click on the Settings tab. In the case of my project, I want to install with root privileges and not require a logout, restart or shutdown.

To accomplish this, I’m choosing the following options in the Settings section:

In the Tag section:

  • Identifier: set as appropriate (for my uninstaller, I’m using com.nexthink.pkg.collector.uninstaller.)
  • Version: set as appropriate (for my uninstaller, I’m using 22.9.1.14. )

In the Post-installation Behavior section:

On Success: should be set to Do Nothing.

In the Options section:

  • Require admin password for installation should be checked
  • Relocatable should be unchecked
  • Overwrite directory permissions should be unchecked
  • Follow symbolic links should be unchecked

Screenshot 2022 12 04 at 3 54 48 PM

 

5. Select the Payload tab. Nothing here should be changed from the defaults.

Screenshot 2022 12 04 at 3 54 58 PM

 

6. Select the Scripts tab.

Under the Additional Resources section, add the following file:

  • The NexThink disk image

Screenshot 2022 12 04 at 3 56 04 PM

Screenshot 2022 12 04 at 3 56 08 PM

 

The last part is telling the NexThink uninstall script to run. For this, you’ll need a postinstall script.

Here’s the postinstall script being used for this example:


This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters


#!/bin/bash
# Description: Script to uninstall NexThink Collector.
ERROR=0
# File Paths
if [[ -f "$(/usr/bin/find $(dirname $0) -maxdepth 1 \( -iname \*\.dmg \))" ]]; then
dmgFile="$(/usr/bin/find $(dirname $0) -maxdepth 1 \( -iname \*\.dmg \))"
fi
dmgMount="$(/usr/bin/mktemp -d /tmp/NexThink_Collector_Uninstaller.XXXX)"
# Remove the trailing slash from the dmgMount variable if needed.
dmgMount=${dmgMount%%/}
# Mount the DMG
/usr/bin/hdiutil attach "$dmgFile" -mountpoint "$dmgMount" -nobrowse -noverify -noautoopen
# Uninstall the NexThink Collector software
"$dmgMount"/uninstaller
# Unmount the DMG
hdiutil detach $dmgMount -force
exit $ERROR
view raw

postinstall

hosted with ❤ by GitHub

If not already selected, select the postinstall script and add it to the project.

Screenshot 2022 12 04 at 3 56 20 PM

Screenshot 2022 12 04 at 3 56 24 PM

 

7. Build the package. (If you don’t know to build, check the Help menu for the Packages User Guide. The information you need is in Chapter 3 – Creating a raw package project and Chapter 10 – Building a project.)

Testing the installer

Once the package has been built, test it by installing it on a test machine which has the following:

  • The NexThink Collector software installed

The end result should be that the NexThink Collector software is removed from the Mac.

Using AutoPkg to build installers for Palo Alto’s GlobalProtect VPN software

$
0
0

As part of some recent testing, I needed to do some work with Palo Alto’s GlobalProtect VPN software. Palo Alto provides an installer package for GlobalProtect, but it has some interesting characteristics as the installer includes three installation options. One is enabled by default and the other two are disabled by default.

The first configuration is the option to install GlobalProtect, the default enabled configuration:

Screenshot 2022 12 08 at 3 47 44 PM

The second configuration is the option to uninstall GlobalProtect, which is disabled by default:

Screenshot 2022 12 08 at 3 49 18 PM

The third configuration is the option to enable the System Extension for GlobalProtect, which is disabled by default:

Screenshot 2022 12 08 at 3 50 35 PM

Note: In the image above, I’ve done some photoshopping because checking the third option to enable the System Extension for GlobalProtect also enables the option to install GlobalProtect. I made the change to the image to hopefully make more clear which option I was discussing.

The options to uninstall GlobalProtect and enable the System Extension for GlobalProtect can be managed by using an installer choices XML file to selectively enable only the desired option. For example, here’s the installer choices XML file for enabling only the option to uninstall GlobalProtect:


This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters


<?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"&gt;
<plist version="1.0">
<array>
<dict>
<key>attributeSetting</key>
<integer>1</integer>
<key>choiceAttribute</key>
<string>selected</string>
<key>choiceIdentifier</key>
<string>second</string>
</dict>
<dict>
<key>attributeSetting</key>
<integer>1</integer>
<key>choiceAttribute</key>
<string>selected</string>
<key>choiceIdentifier</key>
<string>com.paloaltonetworks.globalprotect.uninstall.pkg</string>
</dict>
</array>
</plist>

Here’s the installer choices XML file for enabling only the option to enable the System Extension for GlobalProtect:


This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters


<?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"&gt;
<plist version="1.0">
<array>
<dict>
<key>attributeSetting</key>
<integer>1</integer>
<key>choiceAttribute</key>
<string>selected</string>
<key>choiceIdentifier</key>
<string>third</string>
</dict>
<dict>
<key>attributeSetting</key>
<integer>1</integer>
<key>choiceAttribute</key>
<string>selected</string>
<key>choiceIdentifier</key>
<string>com.paloaltonetworks.globalprotect.systemext.pkg</string>
</dict>
</array>
</plist>

Using these options, I was able to build recipes for AutoPkg which would automatically build three installer packages:

  • An installer which installs GlobalProtect.
  • An installer which uninstalls GlobalProtect.
  • An installer which enables the System Extension for GlobalProtect.

The reason I chose to do this is that using AutoPkg to create these additional installer packages should help ensure any changes that Palo Alto makes to GlobalProtect’s uninstall and System Extension enablement will automatically be available whenever a new version of GlobalProtect is picked up by AutoPkg. In turn, this should save work for those deploying GlobalProtect because now they don’t need to figure out what may have changed between GlobalProtect releases. For more details, please see below the jump.

There is an existing AutoPkg .download recipe for GlobalProtect, available via the link below:

https://github.com/autopkg/peshay-recipes/blob/master/PaloAlto/GlobalProtect.download.recipe

Since that part of the recipe setup is already done, I focused on building AutoPkg .pkg recipes. For the example recipe shown below which handles creating the installer which installs GlobalProtect, the recipe won’t make any changes to the downloaded installer package beyond renaming it. This is because by default, the GlobalProtect installer package installs GlobalProtect.


This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters


<?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"&gt;
<plist version="1.0">
<dict>
<key>Description</key>
<string>Downloads the latest version of Palo Alto's GlobalProtect installer package and renames the package with version. Requires the use of HOSTNAME to point at your GlobalProtect instance. Use pkg_path in parent recipes for package with version.</string>
<key>Identifier</key>
<string>com.company.pkg.GlobalProtect</string>
<key>Input</key>
<dict>
<key>NAME</key>
<string>GlobalProtect</string>
<key>VENDOR</key>
<string>PaloAlto</string>
<key>SOFTWARETITLE</key>
<string>GlobalProtect</string>
</dict>
<key>MinimumVersion</key>
<string>1.0.0</string>
<key>ParentRecipe</key>
<string>com.company.download.GlobalProtect</string>
<key>Process</key>
<array>
<dict>
<key>Arguments</key>
<dict>
<key>destination_path</key>
<string>%RECIPE_CACHE_DIR%/unpack</string>
<key>flat_pkg_path</key>
<string>%pathname%</string>
</dict>
<key>Processor</key>
<string>FlatPkgUnpacker</string>
</dict>
<dict>
<key>Arguments</key>
<dict>
<key>pattern</key>
<string>%RECIPE_CACHE_DIR%/unpack/*gp.pkg</string>
</dict>
<key>Processor</key>
<string>FileFinder</string>
</dict>
<dict>
<key>Arguments</key>
<dict>
<key>destination_path</key>
<string>%RECIPE_CACHE_DIR%/payload/GlobalProtect.app</string>
<key>pkg_payload_path</key>
<string>%found_filename%/Payload</string>
</dict>
<key>Processor</key>
<string>PkgPayloadUnpacker</string>
</dict>
<dict>
<key>Arguments</key>
<dict>
<key>input_plist_path</key>
<string>%RECIPE_CACHE_DIR%/payload/GlobalProtect.app/Contents/Info.plist</string>
<key>plist_version_key</key>
<string>CFBundleShortVersionString</string>
</dict>
<key>Processor</key>
<string>Versioner</string>
</dict>
<dict>
<key>Processor</key>
<string>PkgCopier</string>
<key>Arguments</key>
<dict>
<key>source_pkg</key>
<string>%pathname%</string>
<key>pkg_path</key>
<string>%RECIPE_CACHE_DIR%/%VENDOR%_%SOFTWARETITLE%_%version%.pkg</string>
</dict>
</dict>
<dict>
<key>Processor</key>
<string>PathDeleter</string>
<key>Arguments</key>
<dict>
<key>path_list</key>
<array>
<string>%RECIPE_CACHE_DIR%/unpack</string>
<string>%RECIPE_CACHE_DIR%/payload</string>
</array>
</dict>
</dict>
</array>
</dict>
</plist>

The second and third .pkg recipes will wrap the downloaded installer package inside a second installer package, along with the following files which will also be stored in the second installer package:

  1. An installer choices XML file
  2. A postinstall script which will install the downloaded installer using the options configured by the installer choices XML file.

AutoPkg recipe to create an installer package which uninstalls GlobalProtect:


This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters


<?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"&gt;
<plist version="1.0">
<dict>
<key>Description</key>
<string>Downloads the current release version of the Global Protect VPN client and builds an installer package which uninstalls Global Protect.</string>
<key>Identifier</key>
<string>com.company.pkg.GlobalProtect.uninstall</string>
<key>Input</key>
<dict>
<key>NAME</key>
<string>GlobalProtect</string>
<key>VENDOR</key>
<string>PaloAlto</string>
<key>SOFTWARETITLE1</key>
<string>GlobalProtect</string>
<key>SOFTWARETITLE2</key>
<string>Uninstaller</string>
</dict>
<key>MinimumVersion</key>
<string>1.0.0</string>
<key>ParentRecipe</key>
<string>com.company.download.GlobalProtect</string>
<key>Process</key>
<array>
<dict>
<key>Arguments</key>
<dict>
<key>destination_path</key>
<string>%RECIPE_CACHE_DIR%/unpack</string>
<key>flat_pkg_path</key>
<string>%pathname%</string>
</dict>
<key>Processor</key>
<string>FlatPkgUnpacker</string>
</dict>
<dict>
<key>Arguments</key>
<dict>
<key>pattern</key>
<string>%RECIPE_CACHE_DIR%/unpack/*gp.pkg</string>
</dict>
<key>Processor</key>
<string>FileFinder</string>
</dict>
<dict>
<key>Arguments</key>
<dict>
<key>destination_path</key>
<string>%RECIPE_CACHE_DIR%/payload/GlobalProtect.app</string>
<key>pkg_payload_path</key>
<string>%found_filename%/Payload</string>
</dict>
<key>Processor</key>
<string>PkgPayloadUnpacker</string>
</dict>
<dict>
<key>Arguments</key>
<dict>
<key>input_plist_path</key>
<string>%RECIPE_CACHE_DIR%/payload/GlobalProtect.app/Contents/Info.plist</string>
<key>plist_version_key</key>
<string>CFBundleShortVersionString</string>
</dict>
<key>Processor</key>
<string>Versioner</string>
</dict>
<dict>
<key>Processor</key>
<string>PkgRootCreator</string>
<key>Arguments</key>
<dict>
<key>pkgroot</key>
<string>%RECIPE_CACHE_DIR%/pkgroot</string>
<key>pkgdirs</key>
<dict>
<key>Scripts</key>
<string>0755</string>
</dict>
</dict>
</dict>
<dict>
<key>Processor</key>
<string>FileMover</string>
<key>Arguments</key>
<dict>
<key>source</key>
<string>%RECIPE_CACHE_DIR%/pkgroot/Scripts</string>
<key>target</key>
<string>%RECIPE_CACHE_DIR%/Scripts</string>
</dict>
</dict>
<dict>
<key>Processor</key>
<string>PkgCopier</string>
<key>Arguments</key>
<dict>
<key>source_pkg</key>
<string>%pathname%</string>
<key>pkg_path</key>
<string>%RECIPE_CACHE_DIR%/Scripts/GlobalProtect.pkg</string>
</dict>
</dict>
<dict>
<key>Processor</key>
<string>FileCreator</string>
<key>Arguments</key>
<dict>
<key>file_path</key>
<string>%RECIPE_CACHE_DIR%/Scripts/uninstall_global_protect.xml</string>
<key>file_mode</key>
<string>0755</string>
<key>file_content</key>
<string>&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
&lt;!DOCTYPE plist PUBLIC &quot;-//Apple//DTD PLIST 1.0//EN&quot; &quot;http://www.apple.com/DTDs/PropertyList-1.0.dtd&quot;&gt;
&lt;plist version=&quot;1.0&quot;&gt;
&lt;array&gt;
&lt;dict&gt;
&lt;key&gt;attributeSetting&lt;/key&gt;
&lt;integer&gt;1&lt;/integer&gt;
&lt;key&gt;choiceAttribute&lt;/key&gt;
&lt;string&gt;selected&lt;/string&gt;
&lt;key&gt;choiceIdentifier&lt;/key&gt;
&lt;string&gt;second&lt;/string&gt;
&lt;/dict&gt;
&lt;dict&gt;
&lt;key&gt;attributeSetting&lt;/key&gt;
&lt;integer&gt;1&lt;/integer&gt;
&lt;key&gt;choiceAttribute&lt;/key&gt;
&lt;string&gt;selected&lt;/string&gt;
&lt;key&gt;choiceIdentifier&lt;/key&gt;
&lt;string&gt;com.paloaltonetworks.globalprotect.uninstall.pkg&lt;/string&gt;
&lt;/dict&gt;
&lt;/array&gt;
&lt;/plist&gt;</string>
</dict>
</dict>
<dict>
<key>Processor</key>
<string>FileCreator</string>
<key>Arguments</key>
<dict>
<key>file_path</key>
<string>%RECIPE_CACHE_DIR%/Scripts/postinstall</string>
<key>file_mode</key>
<string>0755</string>
<key>file_content</key>
<string>#!/bin/bash
PKG="${0%/*}/GlobalProtect.pkg"
ChoiceChangesXMLFile="${0%/*}/uninstall_global_protect.xml"
ERROR=0
if [[ -f "$PKG" ]]; then
/usr/sbin/installer -pkg "$PKG" -applyChoiceChangesXML "$ChoiceChangesXMLFile" -target "$3"
if [[ $? -ne 0 ]]; then
/usr/bin/logger -t "${0##*/}" "ERROR! Installation of package $PKG failed"
ERROR=1
fi
else
/usr/bin/logger -t "${0##*/}" "ERROR! Package $PKG not found"
ERROR=1
fi
exit $ERROR</string>
</dict>
</dict>
<dict>
<key>Processor</key>
<string>PkgCreator</string>
<key>Arguments</key>
<dict>
<key>pkg_request</key>
<dict>
<key>pkgroot</key>
<string>%RECIPE_CACHE_DIR%/pkgroot</string>
<key>pkgname</key>
<string>%VENDOR%_%SOFTWARETITLE1%_%SOFTWARETITLE2%_%version%</string>
<key>pkgtype</key>
<string>flat</string>
<key>id</key>
<string>com.company.GlobalProtectUninstall.pkg</string>
<key>options</key>
<string>purge_ds_store</string>
<key>scripts</key>
<string>Scripts</string>
<key>version</key>
<string>%version%</string>
</dict>
</dict>
</dict>
<dict>
<key>Arguments</key>
<dict>
<key>path_list</key>
<array>
<string>%RECIPE_CACHE_DIR%/pkgroot</string>
<string>%RECIPE_CACHE_DIR%/payload</string>
<string>%RECIPE_CACHE_DIR%/Scripts</string>
<string>%RECIPE_CACHE_DIR%/unpack</string>
</array>
</dict>
<key>Processor</key>
<string>PathDeleter</string>
</dict>
</array>
</dict>
</plist>

AutoPkg recipe to create an installer package which enables the System Extension for GlobalProtect:


This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters


<?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"&gt;
<plist version="1.0">
<dict>
<key>Description</key>
<string>Downloads the current release version of the Global Protect VPN client and builds an installer package which enables the Global Protect system extension.</string>
<key>Identifier</key>
<string>com.company.pkg.GlobalProtect.systemextension</string>
<key>Input</key>
<dict>
<key>NAME</key>
<string>GlobalProtect</string>
<key>VENDOR</key>
<string>PaloAlto</string>
<key>SOFTWARETITLE1</key>
<string>GlobalProtect</string>
<key>SOFTWARETITLE2</key>
<string>System</string>
<key>SOFTWARETITLE3</key>
<string>Extension</string>
<key>SOFTWARETITLE4</key>
<string>Enabler</string>
</dict>
<key>MinimumVersion</key>
<string>1.0.0</string>
<key>ParentRecipe</key>
<string>com.company.download.GlobalProtect</string>
<key>Process</key>
<array>
<dict>
<key>Arguments</key>
<dict>
<key>destination_path</key>
<string>%RECIPE_CACHE_DIR%/unpack</string>
<key>flat_pkg_path</key>
<string>%pathname%</string>
</dict>
<key>Processor</key>
<string>FlatPkgUnpacker</string>
</dict>
<dict>
<key>Arguments</key>
<dict>
<key>pattern</key>
<string>%RECIPE_CACHE_DIR%/unpack/*gp.pkg</string>
</dict>
<key>Processor</key>
<string>FileFinder</string>
</dict>
<dict>
<key>Arguments</key>
<dict>
<key>destination_path</key>
<string>%RECIPE_CACHE_DIR%/payload/GlobalProtect.app</string>
<key>pkg_payload_path</key>
<string>%found_filename%/Payload</string>
</dict>
<key>Processor</key>
<string>PkgPayloadUnpacker</string>
</dict>
<dict>
<key>Arguments</key>
<dict>
<key>input_plist_path</key>
<string>%RECIPE_CACHE_DIR%/payload/GlobalProtect.app/Contents/Info.plist</string>
<key>plist_version_key</key>
<string>CFBundleShortVersionString</string>
</dict>
<key>Processor</key>
<string>Versioner</string>
</dict>
<dict>
<key>Processor</key>
<string>PkgRootCreator</string>
<key>Arguments</key>
<dict>
<key>pkgroot</key>
<string>%RECIPE_CACHE_DIR%/pkgroot</string>
<key>pkgdirs</key>
<dict>
<key>Scripts</key>
<string>0755</string>
</dict>
</dict>
</dict>
<dict>
<key>Processor</key>
<string>FileMover</string>
<key>Arguments</key>
<dict>
<key>source</key>
<string>%RECIPE_CACHE_DIR%/pkgroot/Scripts</string>
<key>target</key>
<string>%RECIPE_CACHE_DIR%/Scripts</string>
</dict>
</dict>
<dict>
<key>Processor</key>
<string>PkgCopier</string>
<key>Arguments</key>
<dict>
<key>source_pkg</key>
<string>%pathname%</string>
<key>pkg_path</key>
<string>%RECIPE_CACHE_DIR%/Scripts/GlobalProtect.pkg</string>
</dict>
</dict>
<dict>
<key>Processor</key>
<string>FileCreator</string>
<key>Arguments</key>
<dict>
<key>file_path</key>
<string>%RECIPE_CACHE_DIR%/Scripts/install_system_extensions.xml</string>
<key>file_mode</key>
<string>0755</string>
<key>file_content</key>
<string>&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
&lt;!DOCTYPE plist PUBLIC &quot;-//Apple//DTD PLIST 1.0//EN&quot; &quot;http://www.apple.com/DTDs/PropertyList-1.0.dtd&quot;&gt;
&lt;plist version=&quot;1.0&quot;&gt;
&lt;array&gt;
&lt;dict&gt;
&lt;key&gt;attributeSetting&lt;/key&gt;
&lt;integer&gt;1&lt;/integer&gt;
&lt;key&gt;choiceAttribute&lt;/key&gt;
&lt;string&gt;selected&lt;/string&gt;
&lt;key&gt;choiceIdentifier&lt;/key&gt;
&lt;string&gt;third&lt;/string&gt;
&lt;/dict&gt;
&lt;dict&gt;
&lt;key&gt;attributeSetting&lt;/key&gt;
&lt;integer&gt;1&lt;/integer&gt;
&lt;key&gt;choiceAttribute&lt;/key&gt;
&lt;string&gt;selected&lt;/string&gt;
&lt;key&gt;choiceIdentifier&lt;/key&gt;
&lt;string&gt;com.paloaltonetworks.globalprotect.systemext.pkg&lt;/string&gt;
&lt;/dict&gt;
&lt;/array&gt;
&lt;/plist&gt;</string>
</dict>
</dict>
<dict>
<key>Processor</key>
<string>FileCreator</string>
<key>Arguments</key>
<dict>
<key>file_path</key>
<string>%RECIPE_CACHE_DIR%/Scripts/postinstall</string>
<key>file_mode</key>
<string>0755</string>
<key>file_content</key>
<string>#!/bin/bash
PKG="${0%/*}/GlobalProtect.pkg"
ChoiceChangesXMLFile="${0%/*}/install_system_extensions.xml"
ERROR=0
if [[ -f "$PKG" ]]; then
/usr/sbin/installer -pkg "$PKG" -applyChoiceChangesXML "$ChoiceChangesXMLFile" -target "$3"
if [[ $? -ne 0 ]]; then
/usr/bin/logger -t "${0##*/}" "ERROR! Installation of package $PKG failed"
ERROR=1
fi
else
/usr/bin/logger -t "${0##*/}" "ERROR! Package $PKG not found"
ERROR=1
fi
exit $ERROR</string>
</dict>
</dict>
<dict>
<key>Processor</key>
<string>PkgCreator</string>
<key>Arguments</key>
<dict>
<key>pkg_request</key>
<dict>
<key>pkgroot</key>
<string>%RECIPE_CACHE_DIR%/pkgroot</string>
<key>pkgname</key>
<string>%VENDOR%_%SOFTWARETITLE1%_%SOFTWARETITLE2%_%SOFTWARETITLE3%_%SOFTWARETITLE4%_%version%</string>
<key>pkgtype</key>
<string>flat</string>
<key>id</key>
<string>com.company.GlobalProtectSystemExtensionEnable.pkg</string>
<key>options</key>
<string>purge_ds_store</string>
<key>scripts</key>
<string>Scripts</string>
<key>version</key>
<string>%version%</string>
</dict>
</dict>
</dict>
<dict>
<key>Arguments</key>
<dict>
<key>path_list</key>
<array>
<string>%RECIPE_CACHE_DIR%/pkgroot</string>
<string>%RECIPE_CACHE_DIR%/payload</string>
<string>%RECIPE_CACHE_DIR%/Scripts</string>
<string>%RECIPE_CACHE_DIR%/unpack</string>
</array>
</dict>
<key>Processor</key>
<string>PathDeleter</string>
</dict>
</array>
</dict>
</plist>

When does the upgrade to macOS Ventura need admin rights?

$
0
0

Upgrading to macOS Ventura from macOS Monterey or earlier seems like it should be a straightforward process.

1. Open System Preferences

2. Click on Software Update.

Screen Shot 2022 12 19 at 12 40 55 PM

3. If the macOS Ventura upgrade is listed there, click on the Upgrade Now button.

Screen Shot 2022 12 19 at 11 19 59 AM

However, you may get different upgrade experiences depending on whether you are running macOS 12.3 or later, or if you’re running macOS 12.21 or earlier.

macOS 12.3 or later:

1. You see a macOS Ventura installer which is around 6 GBs or less.

Screen Shot 2022 12 19 at 11 23 04 AM

2. When you click Upgrade Now, you are asked to authenticate as a user. Not as a user with administrator privileges, just as a user.

Screen Shot 2022 12 19 at 11 24 31 AM

macOS 12.21 or earlier

1. You see a macOS Ventura installer which is around 12 GBs or more.

Screen Shot 2022 12 19 at 11 18 59 AM

2. It downloads an Install macOS Ventura app to your Mac and installs it in /Applications.

Screen Shot 2022 12 19 at 12 31 54 PM

3. The Install macOS Ventura app automatically launches once download and installation of the application completes.

Screen Shot 2022 12 19 at 12 31 57 PM

4. Running the Install macOS Ventura app will prompt for a user with administrator privileges to authenticate before the upgrade proceeds.

Screen Shot 2022 12 19 at 12 32 17 PM

Why the difference? The reason is that Apple has developed a new software upgrade path to macOS Ventura for Macs running macOS 12.3 or later which doesn’t require the following:

  1. The need to run the macOS Ventura full installer
  2. The requirement to authenticate as an administrator before upgrading from macOS Monterey to macOS Ventura.

Apple did include additional logic for macOS Ventura upgrades for upgrading to Ventura 13.0.0 and 13.0.1, where if a Mac running macOS Monterey 12.3 or later was enrolled with an MDM management solution and was thus in supervised mode, the new software upgrade path was disabled for those Macs.

As of the release of macOS 13.1, this logic no longer applies and supervised Macs may be offered the new upgrade path (which doesn’t require admin rights to upgrade.) 

For more details about this, and information on how to block the macOS Ventura upgrade from appearing in Software Update if your organization needs more time, please see the Apple KBase article linked below:

My colleague Robert Hammen has also written on the topic of delaying upgrades, so if you’re interested in that topic, please see his Medium post linked below:

Identifying Mac laptops and desktops from the command line by checking for a built-in battery

$
0
0

Every so often, it may be necessary for Mac admins to deploy a script that can apply different settings to Mac desktops and laptops. A good example may be using the pmset command to apply Energy Saver settings, where you may want to apply one set of power management settings to laptops and a different set to desktops.


This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters


#!/bin/bash
# Set separate power management settings for desktops and laptops
# If it's a laptop, the power management settings for "Battery" are set to have the computer sleep in 15 minutes,
# disk will spin down in 10 minutes, the display will sleep in 5 minutes and the display itself will dim to
# half-brightness before sleeping. While plugged into the AC adapter, the power management settings for "Charger"
# are set to have the computer never sleep, the disk doesn't spin down, the display sleeps after 30 minutes and
# the display dims before sleeping.
#
# If it's not a laptop (i.e. a desktop), the power management settings are set to have the computer never sleep,
# the disk doesn't spin down, the display sleeps after 30 minutes and the display dims before sleeping.
#
# Detects if this Mac is a laptop or not by checking the model ID for the word "Book" in the name.
IS_LAPTOP=$(/usr/sbin/system_profiler SPHardwareDataType | grep "Model Identifier" | grep "Book")
if [[ -n "$IS_LAPTOP" ]]; then
/usr/bin/pmset -b sleep 15 disksleep 10 displaysleep 5 halfdim 1
/usr/bin/pmset -c sleep 0 disksleep 0 displaysleep 30 halfdim 1
else
/usr/bin/pmset sleep 0 disksleep 0 displaysleep 30 halfdim 1
fi
view raw

powersetings.sh

hosted with ❤ by GitHub

In the example above, the Model Identifier information from the system_profiler command is used to help identify if the Mac is a desktop or laptop. In this case, the Model Identifier information is checked to see if the model identifier contains “Book”.

Screenshot 2022 12 23 at 5 51 44 PM

If it does, it’s a laptop. Otherwise, it’s a desktop:


This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters


/usr/sbin/system_profiler SPHardwareDataType | grep "Model Identifier" | grep "Book"
view raw

gistfile1.txt

hosted with ❤ by GitHub

However, the latest Mac laptops’ model identifier does not contain “Book”. This means that this identification method should no longer be considered reliable.

Screenshot 2022 12 23 at 5 40 48 PM

What’s an alternative way to check? One way is to use the ioreg command to see if the Mac in question has a built-in battery or not. Laptops will have a built-in battery and desktops will not. For more details, please see below the jump.

You can use the ioreg command shown below to query the information for the AppleSmartBattery hardware driver (currently used by macOS for its battery management) and check whether the Mac has a built-in battery or not:


This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters


/usr/sbin/ioreg -c AppleSmartBattery -r | awk '/built-in/ {print $3}'
view raw

gistfile1.txt

hosted with ❤ by GitHub

On a laptop, the following command should return Yes:


This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters


username@computername ~ % /usr/sbin/ioreg -c AppleSmartBattery -r | awk '/built-in/ {print $3}'
Yes
username@computername ~ %
view raw

gistfile1.txt

hosted with ❤ by GitHub

On a desktop, the same command should return no output:


This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters


username@computername ~ % /usr/sbin/ioreg -c AppleSmartBattery -r | awk '/built-in/ {print $3}'
username@computername ~ %
view raw

gistfile1.txt

hosted with ❤ by GitHub

You should be able to use this command to update the example script for setting power management to correctly identify laptops vs. desktops again:


This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters


#!/bin/bash
# Set separate power management settings for desktops and laptops
# If it's a laptop, the power management settings for "Battery" are set to have the computer sleep in 15 minutes,
# disk will spin down in 10 minutes, the display will sleep in 5 minutes and the display itself will dim to
# half-brightness before sleeping. While plugged into the AC adapter, the power management settings for "Charger"
# are set to have the computer never sleep, the disk doesn't spin down, the display sleeps after 30 minutes and
# the display dims before sleeping.
#
# If it's not a laptop (i.e. a desktop), the power management settings are set to have the computer never sleep,
# the disk doesn't spin down, the display sleeps after 30 minutes and the display dims before sleeping.
#
# Detects if this Mac is a laptop or not by checking for a built-in battery.
IS_LAPTOP=$(/usr/sbin/ioreg -c AppleSmartBattery -r | awk '/built-in/ {print $3}')
if [[ -n "$IS_LAPTOP" ]]; then
/usr/bin/pmset -b sleep 15 disksleep 10 displaysleep 5 halfdim 1
/usr/bin/pmset -c sleep 0 disksleep 0 displaysleep 30 halfdim 1
else
/usr/bin/pmset sleep 0 disksleep 0 displaysleep 30 halfdim 1
fi

Note: The AppleSmartBattery hardware driver is being queried by the ioreg command to gather this information. If Apple ever changes the name or the functionality of the AppleSmartBattery hardware driver, this method may stop working and need to be updated for the new name or functionality.

Apple Device Management Second Edition book available for pre-order from Amazon

$
0
0

As mentioned previously, Charles Edge and I are releasing a new Second Edition of our Apple Device Management book. I’m delighted to announce it’s available for pre-ordering on Amazon via the link below:

https://a.co/d/bnCAr5W

This quality publication, just like First Edition, is stuffed with useful information, sure to be a collectors’ item and suitable for any gift-giving occasion. Please order yours today!

Granting Volume Owner status on Apple Silicon Macs

$
0
0

macOS on Apple Silicon Macs includes a concept known as volume ownership. You must be a volume owner to perform the following tasks on an Apple Silicon Mac:

* There may be multiple installations of macOS on one Apple Silicon Mac; each macOS install would have their own startup security policy.

For more information on volume ownership, please see Apple’s Platform Deployment article linked below:

https://support.apple.com/guide/deployment/use-secure-and-bootstrap-tokens-dep24dbdcf9e/web (see the Volume ownership section.)

How do you get volume ownership though? It turns out that Apple has this currently set up on macOS as a two-fer deal: If an account account has Secure Token, it is also granted volume ownership. For more details, please see below the jump.

To see which users on the Mac have Secure Token, run the following command:



This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters


/usr/sbin/diskutil apfs listCryptoUsers /
view raw

gistfile1.txt

hosted with ❤ by GitHub

The user accounts with Secure Token assigned should appear listed with the following information:

  • Type: Local Open Directory User
  • Volume Owner: Yes

Screenshot-2023-03-10-at-4.42.10-PM.png

In place of the account’s username, the account’s assigned UUID identifier (also referred to as a GeneratedUID) is listed. To get the account username, run the following command with the UUID identifier in the appropriate place:



This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters


/usr/bin/dscl . -search /Users GeneratedUID UUID_goes_here | awk '{print $1}' | head -n 1
view raw

gistfile1.txt

hosted with ❤ by GitHub

Screenshot-2023-03-10-at-4.42.11-PM.png

If the account you want to be a Volume Owner isn’t listed, you can check the account’s Secure Token status by running the following command:



This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters


/usr/sbin/sysadminctl -secureTokenStatus username_goes_here
view raw

gistfile1.txt

hosted with ❤ by GitHub

If the account does not have Secure Token assigned, the output of the command should tell you this.

Screenshot-2023-03-10-at-4.52.54-PM.png

To assign Secure Token (and Volume Owner) to the desired account, run the following command:



This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters


/usr/sbin/sysadminctl -secureTokenOn username_goes_here -password password_goes_here -adminUser user_with_secure_token_goes_here -adminPassword admin_password_goes_here
view raw

gistfile1.txt

hosted with ❤ by GitHub

Screenshot-2023-03-10-at-4.54.53-PM.png

If you want to be prompted for passwords in place of including them as part of the command in plaintext, enter a dash ( ) where you would otherwise enter the relevant account’s password when running the following command:



This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters


/usr/sbin/sysadminctl -secureTokenOn username_goes_here -password – -adminUser user_with_secure_token_goes_here -adminPassword –
view raw

gistfile1.txt

hosted with ❤ by GitHub

Once this has been done, you can verify that Secure Token has been assigned to the desired account by running the following command:



This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters


/usr/sbin/sysadminctl -secureTokenStatus username_goes_here
view raw

gistfile1.txt

hosted with ❤ by GitHub

The output should now tell you that Secure Token has been assigned to the account.

Screenshot-2023-03-10-at-5.09.41-PM.png

To verify that the desired account is now also a Volume Owner, run the following command:



This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters


/usr/sbin/diskutil apfs listCryptoUsers /
view raw

gistfile1.txt

hosted with ❤ by GitHub

You should see a new entry listed with the following information:

  • Type: Local Open Directory User
  • Volume Owner: Yes

Screenshot-2023-03-10-at-5.21.10-PM.png

To get the account username, run the following command with the UUID identifier in the appropriate place:



This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters


/usr/bin/dscl . -search /Users GeneratedUID UUID_goes_here | awk '{print $1}' | head -n 1
view raw

gistfile1.txt

hosted with ❤ by GitHub

The desired account’s username should appear in the output.

Screenshot-2023-03-10-at-5.21.11-PM.png


Creating a sysdiagnose file using Activity Monitor

$
0
0

The sysdiagnose tool is used for gathering a large amount of diagnostic files and logging, and it’s often very useful when it comes to figuring out why a problem is happening. However, it can sometimes be challenging to get a sysdiagnose-generated file from someone who is not comfortable with using the Terminal as the usual method for generating a sysdiagnose file involves opening the Terminal and running commands there.

Fortunately, there’s also a way to generate a sysdiagnose file using Activity Monitor. This may be an alternate way to help get you the desired sysdiagnose file from someone who normally wouldn’t ever use the Terminal on macOS. For more details, please see below the jump.

Apple includes how to generate a sysdiagnose file as part of the Activity Monitor User Guide. For a step by step guide, please use the procedure below:

1. Launch Activity Monitor.

Screenshot-2023-03-16-at-11.32.05-AM.png

2. Choose a method to generate the sysdiagnose file. There’s an option available in the View menu, where you can choose Run System Diagnostics…

Screenshot-2023-03-16-at-11.34.16-AM.png

Another option is click the System diagnostics options pop-up menu (…) and select System Diagnostics…

Screenshot-2023-03-16-at-11.33.04-AM.png

3. Click OK at the privacy agreement screen.

Screenshot-2023-03-16-at-11.35.04-AM.png

After agreeing at the privacy agreement screen, a sysdiagnose file will be generated. This is a process that may take a few minutes to complete.

Screenshot-2023-03-16-at-11.36.30-AM.png

Once the sysdiagnose file has been created, a Finder window will appear showing the location of the sysdiagnose file, which will be a compressed file named something similar to what’s shown below:

sysdiagnose_2023.03.16_11-35-09-0400_macOS_iMacPro1-1_22D68.tar.gz

Screenshot-2023-03-16-at-11.38.04-AM.png

macOS Ventura 13.3 alters expected behavior for Finder’s Open With functionality for macOS installer packages

$
0
0

I’m a frequent user of macOS’s Open With functionality, where I can control-click on a file and select what app I want to open the file with.

Screenshot 2023 04 01 at 5 05 04 PM

Among the files I’m used to doing this with are installer package files. However, I noticed that as of macOS 13.3, this mostly stopped working as the only choice I now had for installer packages was the Installer app. Here’s how it looks on macOS 13.2.1, on a Mac with the Suspicious Package application installed:

Screenshot 2023 04 01 at 4 24 19 PM

Here’s how it looks on macOS 13.3, on a Mac with the Suspicious Package application installed:

Screenshot 2023 04 01 at 4 38 03 PM

When I looked in a Get Info window for an installer package on macOS 13.3, the Open with: functionality was both grayed out and set to Installer.

Screenshot 2023 04 01 at 4 50 43 PM

Normally I would have suspected a bug in macOS 13.3, but according to Randy Saldinger of Mothers Ruin Software, this appears to be an undocumented change by Apple in macOS 13.3.

Screenshot 2023 04 01 at 5 12 50 PM

For more details, please see below the jump.

As of macOS 13.3, a new LaunchServices key in the CFBundleDocumentTypes dictionary, named LSIsAppleDefaultNoOverrideForType, appears to have been introduced. This new key so far only appears in the following file:

/System/Library/CoreServices/CoreTypes.bundle/Contents/Resources/Exceptions.plist

This key is applied to all three package document types used by the Installer app, which means it covers all known macOS installer package files (both flat packages and bundle-style packages.)

Screenshot 2023 04 01 at 4 47 53 PM

The new key appears to affect how LaunchServices manages the Open With functionality specifically in the context of installer packages.

There are still ways outside of the Open With functionality to open an installer package in a desired application. One of the ways is to use the open command in Terminal. For example, if you had an installer package named example.pkg stored on your desktop and you wanted to open the installer package in the Suspicious Package application, you could run the command below:



This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters


open -a "/Applications/Suspicious Package.app" ~/Desktop/example.pkg
view raw

gistfile1.txt

hosted with ❤ by GitHub

Screenshot 2023 04 01 at 4 41 57 PM

The Suspicious Package application should subsequently open and display information about the installer package you had specified.

Screenshot 2023 04 01 at 4 42 43 PM

Migrating an APNS certificate from one Apple ID to another Apple ID

$
0
0

As part of a recent change, I needed to migrate an APNS certificate from being associated with one Apple ID to now being associated with another Apple ID. Apple has a KBase article available which provides contact information for this, which is available via the link below:

https://support.apple.com/HT208643

For those folks with AppleCare support plans, you can also submit a ticket to AppleCare. That’s the route I took. Regardless of which support avenue you pursue, Apple will request the following information from you.

  • APNS Certificate Subject DN
  • APNS Certificate CN
  • APNS Certificate Serial Number
  • APNS Certificate Expiration Date
  • The Apple ID you want to migrate from
  • The Apple ID you want to migrate to

For more information, please see below the jump:

You can obtain the following information from the Apple Push Certificates Portal:

  • APNS Certificate Subject DN
  • APNS Certificate CN
  • APNS Certificate Serial Number
  • APNS Certificate Expiration Date

 

To see how to do this, please use the following procedure:

1. Log into the Apple Push Certificates Portal using the Apple ID you want to migrate from.

Screenshot 2023 04 11 at 3 48 59 PM

 

2. Make a note of the current certificate’s expiration date.

Screenshot 2023 04 11 at 3 49 59 PM

 

3. Click the ( i ) button to display the certificate information.

Screenshot 2023 04 11 at 3 50 22 PM

 

4. Make a note of the APNS certificate’s serial number.

Screenshot 2023 04 11 at 3 50 23 PM

 

5. Make a note of the APNS certificate’s Certificate Subject DN.

Note: Even though it may be displayed in the Portal site as being multiple lines, the Certificate Subject DN should be a one-line entry when you send it to Apple.

Screenshot 2023 04 11 at 3 50 24 PM

 

 

 

6. Make a note of the APNS certificate’s CN.

Note: The CN is included as part of the Certificate Subject DN information. It will be a string with information similar to this:



This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters


CN=APSP:0e77f39b-e9c8-42f9-8e8b-b5508c4abe95
view raw

gistfile1.txt

hosted with ❤ by GitHub

Screenshot 2023 04 11 at 3 50 25 PM

 

 

For example, if you have an APNS certificate with the following information:



This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters


APNS Certificate Subject DN: C=US, CN=APSP:dc1a3263-443c-4779-a3c3-18c95dd11264, UID=com.apple.mgmt.External.dc1a3263-443c-4779-a3c3-18c95dd11264
APNS Certificate Serial Number: 3bb763753df5d8dd
APNS Certificate Expiration Date: January 4, 2024
view raw

gistfile1.txt

hosted with ❤ by GitHub

 

You would convert that to the following information for Apple:



This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters


Serial Number: 3bb763753df5d8dd
Subject CN: CN=APSP:dc1a3263-443c-4779-a3c3-18c95dd11264
Subject DN: C=US, CN=APSP:dc1a3263-443c-4779-a3c3-18c95dd11264, UID=com.apple.mgmt.External.dc1a3263-443c-4779-a3c3-18c95dd11264
Expiration Date: January 4, 2024
view raw

gistfile1.txt

hosted with ❤ by GitHub

 

The last part is identifying the Apple ID you want to migrate from, and the Apple ID you want to migrate to. For example, if you want to migrate an APNS certificate with the information listed above from an Apple ID of oldappleid@company.com to an Apple ID of newappleid@company.com, you could send in the following request via email:



This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters


Email subject: [Apple Push Notification Service] Transferring APNS certificate with serial number 3bb763753df5d8dd from one Apple ID to another Apple ID
Email body:
I need to transfer the following APNS certificate from one Apple ID to another Apple ID:
Serial Number: 3bb763753df5d8dd
Subject CN: CN=APSP:dc1a3263-443c-4779-a3c3-18c95dd11264
Subject DN: C=US, CN=APSP:dc1a3263-443c-4779-a3c3-18c95dd11264, UID=com.apple.mgmt.External.dc1a3263-443c-4779-a3c3-18c95dd11264
Expiration Date: January 4, 2024
Current Apple ID: oldappleid@company.com
New Apple ID: newappleid@company.com
Please let me know if you need any additional information.
Thanks,
Your Name Goes Here
view raw

gistfile1.txt

hosted with ❤ by GitHub

 

That should provide all the information Apple should need for a successful migration of an APNS certificate.

Using the plutil command line tool to work with JSON on macOS Monterey and later

$
0
0

One of the issues Mac admins may face is working with JSON files as part of shell scripting. There are several solutions to this problem, including using the third-party jq command line tool and Apple’s JavaScript for Automation (JXA) interface. For posts on using these solutions, please see the links below:

jq:

JXA:

Another available option is to use the plutil command line tool on macOS Monterey and later to do the following:

  • Read values from JSON files
  • Convert plist files in XML format to JSON

For more details, please see below the jump.

If you want to read JSON values from a file, you can use the raw option of plutil‘s -extract function in some cases to extract values from keys in JSON files. For example, you may have a JSON file with the following keys and values:



This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters


{
"checkInFrequency": 0,
"createHooks": false,
"hookLog": false,
"hookPolicies": false,
"createStartupScript": false,
"startupLog": false,
"startupPolicies": false,
"startupSsh": false,
"enableLocalConfigurationProfiles": false
}
view raw

gistfile1.txt

hosted with ❤ by GitHub

You could use the following command to extract the value for the createStartupScript key in the JSON file:



This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters


plutil -extract createStartupScript raw /path/to/filename.json
view raw

gistfile1.txt

hosted with ❤ by GitHub

In that case, you should see the following output:



This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters


username@computername ~ % plutil -extract createStartupScript raw filename.json
false
username@computername ~ %
view raw

gistfile1.txt

hosted with ❤ by GitHub

In cases like this, where you’re dealing with a JSON file with a fairly simple format (without arrays or otherwise nested values), plutil is a good tool which is built into macOS that you can call on to extract the data you need.

Another option is using the plutil tool to write what you need to an XML file, then use plutil‘s -convert functionality to turn it into a JSON file. For folks more experienced with using plutil to write XML to a file than they are with writing JSON, this option may help with a lot of use cases. For example, you could run the following command to accomplish the following:

1. Create an XML file using the plutil tool

2. Add the following key and value, with the value stored in an array as a string:

  • Key: MyKeyHere
  • Value: MyGreatValue



This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters


plutil -create xml1 file.json
plutil -insert MyKeyHere -xml "<array><string>MyGreatValue</string></array>" file.json
view raw

gistfile1.txt

hosted with ❤ by GitHub

That would give you the following XML file:



This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters


<?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"&gt;
<plist version="1.0">
<dict>
<key>MyKeyHere</key>
<array>
<string>MyGreatValue</string>
</array>
</dict>
</plist>
view raw

gistfile1.txt

hosted with ❤ by GitHub

You would then run the following command to have plutil convert the XML in the file into the equivalent JSON:



This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters


plutil -convert json file.json
view raw

gistfile1.txt

hosted with ❤ by GitHub

That would give you the following JSON file:



This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters


{"MyKeyHere":["MyGreatValue"]}
view raw

gistfile1.txt

hosted with ❤ by GitHub

Something to be aware of is that there will be some limitations to this technique. plutil is designed to work with plist files, which means that if something isn’t formatted like it expects, plutil may not know how to handle it. Two limitations I know of are these:

  • NULL values in JSON files – There’s no equivalent for NULL in plist files, so the plutil tool will fail to convert JSON files with NULL values to a plist file in XML format.
  • Plists which contain date or data values – There’s no JSON equivalent of the date or data values in plist files, so plutil will fail to convert plist files in XML format with these values to JSON files.

Hat tip to Pico in the MacAdmins Slack for telling me about these limitations.

Using Script2Pkg to create payload-free installer packages

$
0
0

Payload-free packages are something I’ve discussed from time to time, as I’ve found them to be very useful additions to my Mac admin toolkit. For those not familiar with the concept, payload-free installer packages are installer packages that exist only to run scripts. They don’t install any files, which would be referred to as the installer package’s payload. With no payload included with these installer packages, the installer packages built by this tool are referred to as payload-free.

A while back, I wrote a tool that would let me easily create them from existing scripts named Payload-Free Package Creator.app. The general idea was that you could use this tool to select a script, and then Payload-Free Package Creator.app would create an unsigned payload-free installer package which would run the selected script.

I’m happy to say that my team at work has expanded on that idea and has both built and open-sourced a tool for building payload-free packages named Script2Pkg. Script2Pkg includes the following functions:

  1. Building an unsigned payload-free installer package
  2. Building a signed payload-free installer package
  3. Building a signed and notarized payload-free installer package
  4. Verifying signing and notarization status of any installer package

For more details, please see below the jump.

Using Script2Pkg

You can use the following procedure to create a payload-free installer package using Script2Pkg.

1. Launch the app.

2. Click the Select scripts button.

Screenshot 2023 02 24 at 3 53 26 PM

3. In the window which appears, select the script(s) you want to use and click the Build button.

Screenshot 2023 02 24 at 3 55 37 PM

Note: You can select multiple scripts. Script2Pkg will create one installer package per selected script.

New payload-free installer package(s) will be created and stored in the same location as the script(s) which were selected.

Screenshot 2023 02 24 at 3 56 28 PM

Each installer package will have the same name as the source script.

Screenshot 2023 02 24 at 4 07 15 PM

By default, installer packages created by Script2Pkg will have the following characteristics:

  • Installer packages created by this tool will not leave an installer package receipt when the package is installed.
  • Installer packages created by this tool will create the installer package in the same location as the selected script.
  • Package version will be the following: 1.0.0
  • Package identifier used by the app will begin with the following: corp.sap.Script2Pkg

Note: The remainder of the identifier will be a UUID. For example, using the default settings will may result in a package with the following package identifier:

corp.sap.Script2Pkg.5BA0E8CB-99B2-4FC2-85AE-0C1CC85ACC05

All of these characteristics can be changed, as Script2Pkg has the following configuration options:

  • Notifying you when packages are created.
  • Choosing where newly-created installer packages are stored.
  • How to handle cases where an existing package has the same name as the package you’re creating.
  • Setting a specific package identifier.
  • Setting a specific package version.
  • Choosing if the packages created by this tool will create an installer package receipt.

For more information on how to configure Script2Pkg, please see Script2Pkg‘s help files or the documentation available at the Script2Pkg wiki.

Screenshot 2023 05 02 at 4 31 15 PM

Creating signed payload-free packages

To sign an installer package using a code signing certificate, you will need a Developer ID Installer certificate installed in a keychain on your Mac. Once the Developer ID Installer certificate is installed in a keychain on your Mac, it should be listed next to Development team: in the main app window.

Screenshot 2023 05 02 at 4 08 16 PM

Once you have your Developer ID Installer singing certificate listed in the Script2Pkg window, check the Sign packages checkbox and subsequent installer packages created by Script2Pkg will be signed using the selected signing certificate.

Screenshot 2023 05 02 at 4 08 24 PM

For more details on configuring Script2Pkg for creating signed installer packages, please see Script2Pkg‘s help files or the documentation available at the Script2Pkg wiki.

Creating signed and notarized payload-free packages

To use notarization with Script2Pkg, you’ll need the following:

  1. Script2Pkg configured for installer package signing
  2. Script2Pkg configured for notarization

For more details on configuring Script2Pkg for to use notarization when creating installer packages, please see Script2Pkg‘s help files or the documentation available at the Script2Pkg wiki.

Once Script2Pkg has been configured for signing and notarization, check the Sign packages and the Notarize packages checkboxes. Subsequent installer packages created by Script2Pkg will be signed using the selected signing certificate and notarized.

Screenshot 2023 05 02 at 4 07 40 PM

Verifying installer packages

Script2Pkg is able to check any installer package and report on the following:

  • If the installer package is signed with a valid code signing certificate
  • If the installer package is signed with a valid code signing certificate and notarized

To check an installer package, using the following procedure to validate its status:

1. Under the File menu, select Validate Package Signature…

Screenshot 2023 03 01 at 2 42 53 PM

2. Select the package you want to check and click the Validate button.

Screenshot 2023 03 01 at 2 48 40 PM

Script2Pkg will then report back on the package signing and notarization status.

Package not signed or notarized

Screenshot 2023 05 02 at 4 50 32 PM

Package signed with a valid certificate and not notarized

Screenshot 2023 05 02 at 4 50 00 PM

Package signed with a valid certificate and notarized

Screenshot 2023 05 02 at 4 49 24 PM

Setting user-level global preferences in a macOS configuration profile

$
0
0

When managing user settings with a profile, you often need to define what the preference domain is in order to specify which settings to manage. Usually you can check the CFBundleIdentifier of an application to get the unique identifier used to define the preference domain.

Normally, these unique IDs (and corresponding preference domains) use a reverse-DNS-lookup scheme. For example, the preference domain for Apple’s web browser Safari is the following:

com.apple.Safari

This is derived from:

a. Apple using the following domain name:

apple.com

b. The application in question being named Safari.

Note: This is a convention, rather than a hard and fast requirement, but most applications’ unique identifiers and corresponding preference domains will use this naming convention.

However, there’s an exception to be aware of. macOS uses a special domain to identify settings which should apply to all applications started by the same user. This preference domain is called the NSGlobalDomain, but unlike most preference domains, you don’t use NSGlobalDomain to define the preference domain when trying to manage their settings with a profile. This can cause some confusion when trying to manage these settings. The preference domain in this case is the following:

.GlobalPreferences

For more details, please see below the jump.

You can see an example of this is a profile I’ve written for managing the scrollbar visibility settings for applications.

Screenshot 2023 05 12 at 10 37 23 AM

 

The .GlobalPreferences preference domain can also be used by Jamf Pro’s profiles, in the Application & Custom Settings profile payload.

Screenshot 2023 05 10 at 4 28 36 PM

20 years of Der Flounder

$
0
0

I noticed that I first wrote an entry on this blog twenty years ago. I figured that deserved some commemoration and recognition.

The blog started out as a personal blog (as evidenced by its early entries) and gradually morphed into being what it is today. I plan to keep writing it because it’s still fun for me and I can see that it’s helped folks out. First blog entry was naturally about starting the blog:

Subject? I don’t need no steenking subject!

At the time, I was using LiveJournal as they had a client app for Mac OS X and that made it easy for me to get started with blogging. Over the years, the blog moved a few times:

I moved to WordPress.com in September 2008 and they’ve been graciously hosting me ever since. Thanks, folks.

I’ve been using MarsEdit for at least the last six years to write my blog posts, so thanks also to Daniel Jalkut at Red Sweater for putting out a quality product that helps me stay focused on the writing of a blog post and not the fiddly bits of how to post it.

For my past, present and future readers:

Thank you. This place wouldn’t be what it is without you.


Downloading installer packages from Jamf Pro when no other options are available

$
0
0

Every so often, Mac admins who administer Jamf Pro may run into a situation like this:

  1. They need an installer package For Reasons.
  2. That installer package is only stored on their Jamf Pro server.
  3. They don’t have access to the distribution point which stores their Jamf Pro server’s installer packages.

In a situation like this, you can use a Jamf Pro policy to provide the installer to a specified Mac. For more details, please see below the jump.

To enable downloading an installer to a specified Mac using Jamf Pro, use the following procedure.

1. Create a policy in Jamf Pro.

Screenshot 2023 05 25 at 3 22 50 PM

2. Add the installer package to the policy.

3. Set the installer package’s Action to Cache.

Screenshot 2023 05 25 at 3 21 46 PM

Set policy trigger and scoping as preferred.

Once the policy runs on the Mac(s) it’s scoped to run on, Jamf Pro will download the installer package to the following directory on the Mac(s):

/Library/Application Support/JAMF/Waiting Room

Note: The Waiting Room directory is only accessible using root privileges.

Once the policy finishes running, you can collect the downloaded installer from /Library/Application Support/JAMF/Waiting Room.

Screenshot 2023 05 25 at 4 33 12 PM

AutoPkg recipes for NexThink Collector

$
0
0

A while back, I posted about how to build an installer for NexThink Collector, but my preference is to not do manual packaging if I can avoid it. Instead, my preference is to have AutoPkg handle packaging tasks whenever possible for the following reasons:

  1. I can ensure that the packaging task is handled the same way every time.
  2. Once I have the correct recipe written for AutoPkg, all I should need to do for future versions of the app is to run the AutoPkg recipe, wait a few minutes and then collect a properly-built installer.

With that in mind, I decided to revisit building an installer for NexThink Collector but this time build AutoPkg recipes which handle the following:

  1. Creating an installer package for NexThink Collector
  2. Creating an uninstaller for NexThink Collector

I was able to do this, so for those interested, please see below the jump for more details.

I’ve built several AutoPkg recipes for NexThink Collector:

  • NexThinkCollector.download: Downloads the latest NexThink Collector installer disk image
  • NexThinkCollector.pkg: Builds an installer package for NexThink Collector
  • NexThinkCollectorUninstaller.pkg: Builds an uninstaller package for NexThink Collector 
  • NexThinkCollector.sign: Signs the installer package produced by the NexThinkCollector.pkg recipe
  • NexThinkCollectorUninstaller.sign: Signs the uninstaller package produced by the NexThinkCollectorUninstaller.pkg recipe

All the recipes are available at the following location:

https://github.com/autopkg/rtrouton-recipes/tree/master/NexThinkCollector

The one which needs the most configuration via an AutoPkg override is the following:

  • NexThinkCollector.pkg

This is because the details of installing and configuring NexThink are going to vary between shops, because different shops are going to configure different options for NexThink. The various Input variable options will provide hopefully all the possible configuration options needed.

To map between the Input variables and the NexThink command line installation configuration options, please see the table below:



AutoPkg Input Variable NexThink Command Line Installation Configuration Option What it defines Required
NTSERVERADDRESS address Fully-qualified domain name of your NexThink instance. Yes
NTUDPPORT port UDP port of your NexThink Instance No
NTTCPPORT tcp_port TCP port of your NexThink Instance Yes
NTPROXYPACADDRESS proxy_pac_address The URL of a PAC address for automatic configuration of proxy settings. No
NTPROXYADDRESS proxy_address The fully-qualified domain name of a proxy for manual configuration of proxy settings No
NTPROXYPORT proxy_port The port number where a proxy is listening for connections for manual configuration of proxy settings. No
NTREMOTEACTIONS ra_execution_policy Configuring the NexThink Collector remote actions settings No
NTENGAGE engage Configuring the NexThink Collector campaign settings No
NTASSIGNMENT use_assignment Configuring the NexThink Collector automatic collector assignment No
NTDATAOVERTCP data_over_tcp Configuring NexThink Collector to send all data over TCP No
NTSTRINGTAG string_tag Configure NexThink Collector label (max 2048 characters) to identify an individual or batch installation of Collectors No

 

Note: There are two NexThink command line installation configurations that are not directly covered:

  • rootca
  • key

For the rootca command line installation configuration option, all the documentation I’ve found in the context of installing the NexThink Collector software on macOS is the following:

Graphical installation documentation

  • Root CA: Leave this field empty.

Command-line installation

  • rootca: Not required.

Since the rootca option doesn’t seem to be required at all and there’s no documentation available on how to configure it for the NexThink Collector software on macOS, I’m leaving it out of the AutoPkg recipe.

For the key command line installation configuration option, this functionality is covered by the following required Input variables:



AutoPkg Input Variable What it defines Required
NTCUSTOMERKEYDATA Contents of the NexThink Customer Key file Yes
NTCUSTOMERKEYNAME Filename of the NexThink Customer Key file Yes

 

If both Input variables are filled in, then the postinstall script used by the installer package generated by this AutoPkg recipe will call the key command line installation configuration option and use it to configure the NexThink Collector software with the proper Customer Key information.

To see how this looks in a recipe override of the NexThinkCollector.pkg recipe, let’s create one with the following Input variables set:

  • NTSERVERADDRESS: server.nexthink.com
  • NTTCPPORT: 443
  • NTREMOTEACTIONS: disabled
  • NTENGAGE: disable
  • NTASSIGNMENT: enable
  • NTDATAOVERTCP: enable
  • NTCUSTOMERKEYNAME: nexthink-customer-key.txt
  • NTCUSTOMERKEYDATA: 



This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters


—–BEGIN CUSTOMER KEY—–MIIDhzCCAm+gAwIBAgIEIa+KoTANBgkqhkiG9w0BAQsFADBbMScwJQYDVQQDDB5SZWdlcnkgU2VsZi1TaWduZWQgQ2VydGlmaWNhdGUxIzAhBgNVBAoMGlJlZ2VyeSwgaHR0cHM6Ly9yZWdlcnkuY29tMQswCQYDVQQGEwJVQTAgFw0yMjEyMDIwMDAwMDBaGA8yMTIyMTIwMjIwMDIxMFowSTEVMBMGA1UEAwwMbG9jYWxob3N0LmlvMSMwIQYDVQQKDBpSZWdlcnksIGh0dHBzOi8vcmVnZXJ5LmNvbTELMAkGA1UEBhMCVUEwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDaKRW9KeX4wg/838FkxmzaBjqf1DeKD5GKEqhUKz0y78Wwnsv2zAXGM4UkdZJP9zHtC9/wFQT+lhclDlogxkU9lfMADV7nMdGL0GkJzwMQNS52dPNXDup7/d9yRkyjkV0Pf4t2fJF3igoNXFQuBvuArkNV6hfja2gOEczOSAaJ7L7qRnSahLjciJRaCuEPjwneh3krhOFT+djwuYJMIvBDEqs+gfp4OPDDBtVg2scUUGRmHsC+JAoK+JwqYwB9TNt+9hZtGfDqgZSHebXEfRTguhQpBj0mPTo76EahAbHbXJhV+efg3jt32pZ6qRl8ffrZAjefWEAnOMyXQ7fbL+bpAgMBAAGjYzBhMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBRNHRZG3IKNH0kTRaiVfq6N8Ovp5zAfBgNVHSMEGDAWgBRNHRZG3IKNH0kTRaiVfq6N8Ovp5zANBgkqhkiG9w0BAQsFAAOCAQEAhpbntg+nwhIKgRuUidu/wXn197Ah0Pd4CYYxG5dR9rg8nWObx4QO6ApIH91nUUQVuV6mSTFtfy4yNQzxaROgZP9hDNvhd78D/ewXxp6bN/Xkn+c7SWrs/b1vHb2Dr1sDP4F9SAOrCI6TdoYa8UNhPXXSTt8M/hGSB2oWOpT2FAb2IbdmdYhDaibcJwp+/Had1FLbeDZgdgYCFoZLjws/9E/pIXjSxBYAJLbaQZffrfO5jCe2KesE73iQatW2IPynsFifRGGoMHXVLOfsLA9c2KDGqDmnJ+PvsBSe9rIpSJYC4WjR5Mt8W88kQSj05b9NqCsXmmMDEbD8uVLyKvQihA==—–END CUSTOMER KEY—–

 

With those variables set, the AutoPkg recipe override should look similar to what’s shown below:



This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters


<?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"&gt;
<plist version="1.0">
<dict>
<key>Identifier</key>
<string>local.pkg.NexThinkCollector</string>
<key>Input</key>
<dict>
<key>DOWNLOAD_URL</key>
<string>https://download.nexthink.com/releases/latest/OSX_Collector/Nexthink_Collector.dmg</string>
<key>NAME</key>
<string>NexThink Collector</string>
<key>NTASSIGNMENT</key>
<string>enable</string>
<key>NTCUSTOMERKEYDATA</key>
<string>—–BEGIN CUSTOMER KEY—–MIIDhzCCAm+gAwIBAgIEIa+KoTANBgkqhkiG9w0BAQsFADBbMScwJQYDVQQDDB5SZWdlcnkgU2VsZi1TaWduZWQgQ2VydGlmaWNhdGUxIzAhBgNVBAoMGlJlZ2VyeSwgaHR0cHM6Ly9yZWdlcnkuY29tMQswCQYDVQQGEwJVQTAgFw0yMjEyMDIwMDAwMDBaGA8yMTIyMTIwMjIwMDIxMFowSTEVMBMGA1UEAwwMbG9jYWxob3N0LmlvMSMwIQYDVQQKDBpSZWdlcnksIGh0dHBzOi8vcmVnZXJ5LmNvbTELMAkGA1UEBhMCVUEwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDaKRW9KeX4wg/838FkxmzaBjqf1DeKD5GKEqhUKz0y78Wwnsv2zAXGM4UkdZJP9zHtC9/wFQT+lhclDlogxkU9lfMADV7nMdGL0GkJzwMQNS52dPNXDup7/d9yRkyjkV0Pf4t2fJF3igoNXFQuBvuArkNV6hfja2gOEczOSAaJ7L7qRnSahLjciJRaCuEPjwneh3krhOFT+djwuYJMIvBDEqs+gfp4OPDDBtVg2scUUGRmHsC+JAoK+JwqYwB9TNt+9hZtGfDqgZSHebXEfRTguhQpBj0mPTo76EahAbHbXJhV+efg3jt32pZ6qRl8ffrZAjefWEAnOMyXQ7fbL+bpAgMBAAGjYzBhMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBRNHRZG3IKNH0kTRaiVfq6N8Ovp5zAfBgNVHSMEGDAWgBRNHRZG3IKNH0kTRaiVfq6N8Ovp5zANBgkqhkiG9w0BAQsFAAOCAQEAhpbntg+nwhIKgRuUidu/wXn197Ah0Pd4CYYxG5dR9rg8nWObx4QO6ApIH91nUUQVuV6mSTFtfy4yNQzxaROgZP9hDNvhd78D/ewXxp6bN/Xkn+c7SWrs/b1vHb2Dr1sDP4F9SAOrCI6TdoYa8UNhPXXSTt8M/hGSB2oWOpT2FAb2IbdmdYhDaibcJwp+/Had1FLbeDZgdgYCFoZLjws/9E/pIXjSxBYAJLbaQZffrfO5jCe2KesE73iQatW2IPynsFifRGGoMHXVLOfsLA9c2KDGqDmnJ+PvsBSe9rIpSJYC4WjR5Mt8W88kQSj05b9NqCsXmmMDEbD8uVLyKvQihA==—–END CUSTOMER KEY—–</string>
<key>NTCUSTOMERKEYNAME</key>
<string>nexthink-customer-key.txt</string>
<key>NTDATAOVERTCP</key>
<string>enable</string>
<key>NTENGAGE</key>
<string>disable</string>
<key>NTPROXYADDRESS</key>
<string></string>
<key>NTPROXYPACADDRESS</key>
<string></string>
<key>NTPROXYPORT</key>
<string></string>
<key>NTREMOTEACTIONS</key>
<string>disabled</string>
<key>NTSERVERADDRESS</key>
<string>server.nexthink.com</string>
<key>NTSTRINGTAG</key>
<string></string>
<key>NTTCPPORT</key>
<string>443</string>
<key>NTUDPPORT</key>
<string></string>
<key>SOFTWARETITLE</key>
<string>Collector</string>
<key>VENDOR</key>
<string>NexThink</string>
</dict>
<key>ParentRecipe</key>
<string>com.github.rtrouton.pkg.NexThinkCollector</string>
<key>ParentRecipeTrustInfo</key>
<dict>
<key>non_core_processors</key>
<dict/>
<key>parent_recipes</key>
<dict>
<key>com.github.rtrouton.download.NexThinkCollector</key>
<dict>
<key>path</key>
<string>~/github/rtrouton-recipes/NexThinkCollector/NexThinkCollector.download.recipe</string>
<key>sha256_hash</key>
<string>025505c8c4527bb20515a603a423f323f8230cae5a80c716390aa303a95cb84c</string>
</dict>
<key>com.github.rtrouton.pkg.NexThinkCollector</key>
<dict>
<key>path</key>
<string>~/github/rtrouton-recipes/NexThinkCollector/NexThinkCollector.pkg.recipe</string>
<key>sha256_hash</key>
<string>df09ce476b30b366f8fc02a390ff8123b3d245bd3382cc409903f08b2f3cd252</string>
</dict>
</dict>
</dict>
</dict>
</plist>

 

Once configured, the NexThinkCollector.pkg AutoPkg recipe should work with the NexThinkCollector.download recipe to do the following:

 

  • Download the latest NexThink Collector installer disk image
  • Wrap the disk image and CustomerKey file inside a separate installer package.
  • Use a postinstall script to perform the following actions:
  1. Identify the location of the disk image stored inside the installer package.
  2. Mount the disk image
  3. Identify the location of the csi.app on the mounted disk image.
  4. Identify the location of the CustomerKey file stored inside the installer package.
  5. Use the csi app’s command line tool to configure and run the NexThink-provided installer package on the mounted disk image, to install the NexThink Collector software.
  6. Unmount the disk image.

The other .pkg AutoPkg recipe I wrote is NexThinkCollectorUninstaller.pkg and it is much simpler in terms of operation. It uses the same technique described in my earlier post on how to build a NexThink Collector uninstaller and should not need configuration of any Input variables. The NexThinkCollectorUninstaller.pkg recipe works with the NexThinkCollector.download recipe to do the following:

 

  • Download the latest NexThink Collector installer disk image
  • Wrap the disk image inside a separate installer package.
  • Use a postinstall script to perform the following actions:

 

  1. Identify the location of the disk image stored inside the installer package.
  2. Mount the disk image
  3. Use the uninstall script to uninstall the NexThink Collector software.
  4. Unmount the disk image.

The AutoPkg recipe override should look similar to what’s shown below:



This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters


<?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"&gt;
<plist version="1.0">
<dict>
<key>Identifier</key>
<string>local.pkg.NexThinkCollectorUninstaller</string>
<key>Input</key>
<dict>
<key>DOWNLOAD_URL</key>
<string>https://download.nexthink.com/releases/latest/OSX_Collector/Nexthink_Collector.dmg</string>
<key>NAME</key>
<string>NexThink Collector Uninstaller</string>
<key>SOFTWARETITLE</key>
<string>Collector</string>
<key>SOFTWARETITLE1</key>
<string>Collector</string>
<key>SOFTWARETITLE2</key>
<string>Uninstaller</string>
<key>VENDOR</key>
<string>NexThink</string>
</dict>
<key>ParentRecipe</key>
<string>com.github.rtrouton.pkg.NexThinkCollectorUninstaller</string>
<key>ParentRecipeTrustInfo</key>
<dict>
<key>non_core_processors</key>
<dict/>
<key>parent_recipes</key>
<dict>
<key>com.github.rtrouton.download.NexThinkCollector</key>
<dict>
<key>git_hash</key>
<string>a580ead04110ccc2b938da2373b55e6d85ac9d09</string>
<key>path</key>
<string>~/Library/AutoPkg/RecipeRepos/com.github.autopkg.rtrouton-recipes/NexThinkCollector/NexThinkCollector.download.recipe</string>
<key>sha256_hash</key>
<string>025505c8c4527bb20515a603a423f323f8230cae5a80c716390aa303a95cb84c</string>
</dict>
<key>com.github.rtrouton.pkg.NexThinkCollectorUninstaller</key>
<dict>
<key>git_hash</key>
<string>a580ead04110ccc2b938da2373b55e6d85ac9d09</string>
<key>path</key>
<string>~/Library/AutoPkg/RecipeRepos/com.github.autopkg.rtrouton-recipes/NexThinkCollector/NexThinkCollectorUninstaller.pkg.recipe</string>
<key>sha256_hash</key>
<string>0c614e578c594e2ec0de010d5e95ec791d3fae840d6da0df5c297247f754e522</string>
</dict>
</dict>
</dict>
</dict>
</plist>

WWDC 2023 notes

$
0
0

This week, like 2020, 2021 and 2022, I’m attending Apple’s WWDC 2023 conference from the comforts of home. As part of this, I’m taking notes during the labs and session videos. Due to wanting to stay on the right side of Apple’s NDA, I’ve been posting my notes to Apple’s developer forums rather than to here.

To make it easier for Mac admins to access them, I’ve set up a post in the forums where I’ve linking the various forum posts with my notes. It’s available via the link below:

https://developer.apple.com/forums/thread/731058

Booting into macOS Recovery in UTM on Apple Silicon Macs

$
0
0

On Apple Silicon Macs, VMware Fusion does not yet support running macOS in a VM, so on my Apple Silicon Macs I have switched from using Fusion to using UTM for running macOS VMs. One feature missing until recently was the ability to boot into macOS Recovery in a VM in UTM.

UTM uses’s Apple’s Virtualization framework, which has a startup option for booting into Recovery, but until recently UTM had not implemented this start option for macOS VMs. As of UTM 4.2.5 (the current version as of June 23, 2023), it looks like this option has now been added. For more details, please see below the jump.

To boot a macOS VM into Recovery mode in UTM, please use the procedure shown below.

1. If needed, build a macOS VM in UTM.

2. Once the macOS VM has been created, right-click (or control-click) on the macOS VM in UTM’s VM list.

3. Select the Run Recovery option.

Screenshot 2023 06 23 at 10 42 42 AM

The macOS VM will boot into the Apple Silicon boot menu.

4. Select Options.

Screenshot 2023 06 23 at 10 43 44 AM

5. Click Continue.

Screenshot 2023 06 23 at 10 43 59 AM

The VM should now boot into Recovery.

Screenshot 2023 06 23 at 10 44 07 AM

Manually triggering MDM profile renewal with Jamf Pro

$
0
0

When you enroll a Mac with Jamf Pro, the first profile the Mac receives is named MDM Profile.

Screenshot 2023 06 24 at 10 32 54 AM

It is valid for two years.

Screenshot 2023 06 24 at 10 14 53 AM

After two years, the MDM profile is supposed to automatically renew but sometimes this renewal process is either not triggered or is triggered and does not succeed.

On Jamf Pro, there’s a couple of ways you can manually trigger the MDM renewal to occur. The first is a manual process which uses the Jamf Pro admin console. The second uses both of Jamf Pro’s APIs and can be automated via a Jamf Pro policy. For more details, please see below the jump.

Manually triggering MDM profile renewal using the Jamf Pro admin console

To manually trigger MDM profile renewal using the admin console, please use the procedure shown below.

1. Run a search for the computers you want to renew the MDM profile on.

2. Once you have the desired list, click the Action button.

Screenshot 2023 06 23 at 4 42 30 PM

3. Select Send Remote Commands and click the Next button.

Screenshot 2023 06 23 at 4 42 37 PM

4. Select Renew MDM profile and click the Next button.

Screenshot 2023 06 23 at 4 42 43 PM

5. Once the MDM command has been sent, click the Done button.

Screenshot 2023 06 23 at 4 42 48 PM

Manually triggering MDM profile renewal using Jamf Pro’s APIs

You can also use Jamf Pro’s APIs to script an MDM profile renewal at whatever interval is desired. My approach is the following:

1. Write a script designed to run via a Jamf Pro policy on individual Macs to perform the following tasks:

  • Get the Jamf Pro UDID number of the Mac the script is running on.
  • Use the UDID to send a “Renew MDM profile” MDM command via the API.

Note: This approach uses both the Jamf Pro Classic API and the Jamf Pro API, as the command I’m using to get the Jamf Pro UDID number is using the Classic API and the command used to trigger the MDM profile renewal MDM command is using the Jamf Pro API.

For those who haven’t used the Jamf Pro APIs before, you will need to provide a username and password to the script. This is a security risk, so my recommendation is to carefully evaluate if the risk is worth it for your environment. If it’s not, don’t use this approach.

One way to mitigate this risk is to set up a dedicated account with the least privileges necessary. This method does not eliminate the risk, but it may reduce it to one acceptable in your environment. In my testing, the least privileges are the following:

In Jamf Pro Server Objects:

Computers: Read

In Jamf Pro Server Actions:

Send Command to Renew MDM Profile

2. Create a smart group with the following criteria:

  • Name: MDM Certificate Expiration in Five Days or Less
  • Criteria: MDM Profile Expiration Date
  • Operator: in less than X days
  • Value: 5

Note: You may choose different criteria, but in this example I want to run the script on Macs whose MDM profile expiration dates are five days or less.

Screenshot 2023 06 23 at 4 57 53 PM

3. Set up a Jamf Pro computer policy with the following components:

  • Script: The script to trigger MDM profile renewal
  • Trigger: Recurring Check-In
  • Execution Frequency: Once every day
  • Scoped to: the MDM Certificate Expiration in Five Days or Less smart group.

Note: Execution Frequency can be set as desired for a longer interval, like Once every week or Once every month.

Screenshot 2023 06 24 at 10 20 46 AM

The script is available from following address on GitHub:

https://github.com/rtrouton/rtrouton_scripts/tree/main/rtrouton_scripts/Casper_Scripts/renew_Jamf_Pro_mdm_profile

Using either method (admin console triggered renewal or API triggered renewal), the expected behavior is that the MDM profile for Jamf Pro is renewed and shows an install date of when the MDM renewal was triggered.

Screenshot 2023 06 24 at 10 27 49 AM

Viewing all 764 articles
Browse latest View live