babgond http://www.babgond.com/moonmoon/ 2024-04-26T18:10:40Z Author Der Flounder : Losing a giant https://derflounder.wordpress.com/2024/04/22/losing-a-giant/ 2024-04-22T17:54:56+00:00 rtrouton I learned today that Charles Edge, one of the smartest people I’ve been privileged to know in my life, has passed away. I don’t know more details, but we have lost a kind and magnificent giant in the Mac Admins community and I have lost a good friend. 

It hits all the harder in that his death is so unexpected. Charles was one of the most alive people I knew. He wrote countless books (including a couple editions of Apple Device Management with me as co-author), either ran or appeared in multiple podcasts, had a full time job, contributed multiple open source tools and despite all of that always seemed to have time to talk. My regret is that I didn’t talk to him more often, with our last conversation happening in February.

I already miss you terribly, man. Wherever you are now, I hope you know how much you are loved and missed here.

If you have good memories of Charles, please share them in the comments below.

]]>
Der Flounder : Updated scripts for downloading packages from a JCDS2 distribution point https://derflounder.wordpress.com/2024/03/28/updated-scripts-for-downloading-packages-from-a-jcds2-distribution-point/ 2024-03-28T15:24:52+00:00 rtrouton As part of an earlier post, I had provided scripts for downloading installer packages from a JCDS2 distribution point. I’ve made some updates to the scripts to provide more checking of the installer packages, to hopefully ensure that the downloaded installer package and the package available in the JCDS2 distribution point are exactly the same package.

The original scripts would check the download directory and see if there was an installer package with the same name as an installer package in the JCDS2 distribution point. If there was an installer package with a matching name, download of the installer package was skipped and the script would move on to the next installer package.

In the updated scripts, if there are installer packages already in the download directory which have the same name as an installer package in the JCDS distribution point, the MD5 hash of the existing installer package is checked against the MD5 hash of the installer package stored in the JCDS distribution point. 

If the MD5 hashes match, download of the installer package with the matching name is skipped. If the MD5 hashes do not match, the existing installer package in the download directory is deleted and a fresh copy of the installer package is downloaded. For more details, please see below the jump.

Usage: 

/path/to/Jamf_Pro_JCDS_Installer_Package_Download.sh

The script takes the following actions:

  • Creates a download directory if none has been specified in the script.
  • Uses the Jamf Pro Classic API to download the list of installer packages from the Jamf Pro server.
  • Gets the Jamf Pro ID numbers for the individual installer packages.
  • Uses the Jamf Pro Classic API to get the names of the individual installer packages.
  • Checks to see if a file with a matching name exists in the download directory.
  • If a file with a matching name does not exist in the download directory, use the Jamf Pro API to query the JCDS2 distribution point for the download URL of the installer package and download the installer package.
  • If a file with a matching name exists in the download directory, check the MD5 hash of the file with the matching name and compare it against the MD5 hash of the file in the JCDS2 distribution point. 
    •  If the MD5 hashes match, display a message that the file exists in the download directory. 
    •  If they don’t match, delete the file with the matching name from the download directory, use the Jamf Pro API to query the JCDS2 distribution point for the download URL of the installer package and download the installer package.

The script should provide output similar to this:


username@computername ~ % /path/to/Jamf_Pro_JCDS_Installer_Package_Download.sh
Please enter your Jamf Pro server URL : https://server_name_here.jamfcloud.com
Please enter your Jamf Pro user account : username_goes_here
Please enter the password for the username_goes_here account:
Downloading Google_Chrome_121.0.6167.184.pkg to /var/folders/vd/c27hl4p53j1_5cnv9ynpxp6m0000gn/T/tmp.R7v2rAecOR.
################################################################################################################################################################# 100.0%
Google_Chrome_121.0.6167.184.pkg is available in /var/folders/vd/c27hl4p53j1_5cnv9ynpxp6m0000gn/T/tmp.R7v2rAecOR.
Microsoft_Edge_122.0.2365.92.pkg found in /var/folders/vd/c27hl4p53j1_5cnv9ynpxp6m0000gn/T/tmp.R7v2rAecOR.
Checking MD5 hash of Microsoft_Edge_122.0.2365.92.pkg in /var/folders/vd/c27hl4p53j1_5cnv9ynpxp6m0000gn/T/tmp.R7v2rAecOR to verify match with Microsoft_Edge_122.0.2365.92.pkg on https://server_name_here.jamfcloud.com…
MD5 hash of Microsoft_Edge_122.0.2365.92.pkg in /var/folders/vd/c27hl4p53j1_5cnv9ynpxp6m0000gn/T/tmp.R7v2rAecOR does not match Microsoft_Edge_122.0.2365.92.pkg on https://server_name_here.jamfcloud.com.
Deleting Microsoft_Edge_122.0.2365.92.pkg from /var/folders/vd/c27hl4p53j1_5cnv9ynpxp6m0000gn/T/tmp.R7v2rAecOR.
Downloading Microsoft_Edge_122.0.2365.92.pkg to /var/folders/vd/c27hl4p53j1_5cnv9ynpxp6m0000gn/T/tmp.R7v2rAecOR.
######################################################################################################################################### 100.0%
Microsoft_Edge_122.0.2365.92.pkg is available in /var/folders/vd/c27hl4p53j1_5cnv9ynpxp6m0000gn/T/tmp.R7v2rAecOR.
Microsoft_Office_16.83.pkg found in /var/folders/vd/c27hl4p53j1_5cnv9ynpxp6m0000gn/T/tmp.R7v2rAecOR.
Checking MD5 hash of Microsoft_Office_16.83.pkg in /var/folders/vd/c27hl4p53j1_5cnv9ynpxp6m0000gn/T/tmp.R7v2rAecOR to verify match with Microsoft_Office_16.83.pkg on https://server_name_here.jamfcloud.com…
MD5 hash of Microsoft_Office_16.83.pkg in /var/folders/vd/c27hl4p53j1_5cnv9ynpxp6m0000gn/T/tmp.R7v2rAecOR matches Microsoft_Office_16.83.pkg on https://server_name_here.jamfcloud.com.
Microsoft_Office_16.83.pkg is available in /var/folders/vd/c27hl4p53j1_5cnv9ynpxp6m0000gn/T/tmp.R7v2rAecOR.
Microsoft_OneDrive_24.020.0128.pkg found in /var/folders/vd/c27hl4p53j1_5cnv9ynpxp6m0000gn/T/tmp.R7v2rAecOR.
Checking MD5 hash of Microsoft_OneDrive_24.020.0128.pkg in /var/folders/vd/c27hl4p53j1_5cnv9ynpxp6m0000gn/T/tmp.R7v2rAecOR to verify match with Microsoft_OneDrive_24.020.0128.pkg on https://server_name_here.jamfcloud.com…
MD5 hash of Microsoft_OneDrive_24.020.0128.pkg in /var/folders/vd/c27hl4p53j1_5cnv9ynpxp6m0000gn/T/tmp.R7v2rAecOR matches Microsoft_OneDrive_24.020.0128.pkg on https://server_name_here.jamfcloud.com.
Microsoft_OneDrive_24.020.0128.pkg is available in /var/folders/vd/c27hl4p53j1_5cnv9ynpxp6m0000gn/T/tmp.R7v2rAecOR.
username@computername ~ %

view raw

gistfile1.txt

hosted with ❤ by GitHub

The scripts are available from GitHub at the following location:

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

]]>
Der Flounder : Migrating a Jamf Pro AWS-hosted cloud distribution point to a JCDS2 cloud distribution point https://derflounder.wordpress.com/2024/03/26/migrating-a-jamf-pro-aws-hosted-cloud-distribution-point-to-a-jcds2-cloud-distribution-point/ 2024-03-26T16:54:44+00:00 rtrouton I recently needed to migrate a Jamf Cloud-hosted Jamf Pro instance from using an AWS-hosted cloud distribution point to using a Jamf-hosted JCDS2 cloud distribution point. For those looking at a similar migration, please see below the jump for more details.

Advisory: I strongly advise having Jamf’s Professional Services folks involved if you’re planning a migration like this. The reason is that, as of the current Jamf Pro 11.3.2 release, you can only have one cloud distribution point at a time. A migration like the one I performed will involve a cut-over process which includes having to re-upload your current distribution point’s installer packages to the new JCDS2 distribution point. This process also necessitates having good recent backups for the Jamf Pro instance in question.

With the assistance of Jamf’s Professional Services (particular thanks to Sepie Moinipanah, Leslie Helou and David Raabe for their support), the migration in my case went smoothly. Please see below for the process I followed to migrate from an AWS-hosted cloud distribution point to using a Jamf-hosted JCDS2 cloud distribution point:

Pre-requisites:

Getting the installers from your current distribution point:

There are several ways to get the installers from an AWS-hosted cloud distribution point. The method I chose was to use AWS’s command line tool to sync the contents of the S3 bucket used by the cloud distribution point to a local directory on my workstation:

https://derflounder.wordpress.com/2018/02/15/backing-up-the-contents-of-an-aws-hosted-jamf-pro-cloud-distribution-point-to-a-local-directory/

API Client Role and API Client:

The jamfCPR wiki describes the necessary permissions needed to sync installer packages to a JCDS2 cloud distribution point, in the context of an API Client Role. As a result, I used an API Client Role and API Client in this case because the API Client Role permissions don’t always map one-to-one to the permissions available to Jamf Pro accounts and groups. Please see below for the permissions I set for my API Client Role:

  • Read Cloud Services Settings
  • Read Distribution Points
  • Create Packages
  • Read Packages
  • Update Packages
  • Delete Packages
  • Read Cloud Distribution Point
  • Update Cloud Distribution Point
  • Create Jamf Content Distribution Server Files
  • Read Jamf Content Distribution Server Files
  • Delete Jamf Content Distribution Server Files
  • Jamf Packages Action

Screenshot 2024-03-26 at 9.19.23 AM

Preparing for the distribution point migration:

1. Verify that you have the latest version of jamfCPR available.

Note: If the JCDS2 cloud distribution point you’re migrating to is located outside of the United States, make sure you’re using jamfCPR 5.x or later. jamfCPR 4.12 and earlier is not able to work with JCDS2 cloud distribution points which are hosted in AWS regions outside of the United States.

2. Verify that you have an API Client Role and API Client on the Jamf Pro server which has the correct permissions assigned.

3. Verify that you have a copy of all installers on your current AWS-hosted cloud distribution point stored on the same Mac that you have the jamfCPR app installed on.

4. Work with Jamf to make sure that a backup of your Jamf Pro service is made just prior to doing the migration.

Advisory about the Jamf Pro backup:

At this point, I want to stop for a moment and discuss why that backup of your Jamf Pro service is so important. The reason has to do with how AWS-hosted cloud distribution points are created. When you set one up, Jamf Pro will do the following:

  1. Create an S3 bucket with a randomly-generated name in the US-East-1 AWS region.
  2. Create an associated CloudFront distribution which connects to the S3 bucket created in Step 1.

As the Jamf Pro admin, you don’t get to choose anything in this process and you can’t select an existing S3 bucket. What this means is that the migration goes wrong and you need to revert back to your AWS-hosted cloud distribution point, the only way to do so is to roll back your Jamf Pro service to a point in time before the migration started. You will not be able to go back to using your existing AWS-hosted cloud distribution point without restoring from that backup because there is no way otherwise to have Jamf Pro use that existing AWS-hosted cloud distribution point.

If you try to go back otherwise, Jamf Pro will not use the existing AWS-hosted cloud distribution point. Instead, Jamf Pro will set up a new S3 bucket and CloudFront distribution and you will now have a brand-new and completely empty AWS-hosted cloud distribution point.

Running the migration:

1. Log into your Jamf Pro server as an admin user with all needed rights.

2. Verify that the Cloud Services connection is logged in and appears to be working properly.

3. Go to Settings: Server: Cloud distribution point.

4. Click the Test button and verify that your connection to the cloud distribution point is working correctly.

5. Install something from your Jamf Pro server and verify that installation is working correctly.

6. Verify in your policy logs that the installer is coming from an address which matches something similar to what’s shown below:

https://d2zft6agzhvlnv.cloudfront.net

7. In your Jamf Pro server, go to Settings: Server: Cloud distribution point.

Note: The next step is a point of no return, for reasons described above in the Advisory about the Jamf Pro backup section. Make sure a very recent Jamf Pro backup is available.

8. Select Jamf Cloud and click the Save icon.

9. Click the Test button and verify that your connection to the cloud distribution point is working correctly.

10. Open the jamfCPR app on your Mac.

11. Select the directory containing the downloaded copy of the installers from the existing AWS-hosted cloud distribution point.

12. Set up the connection to your Jamf Pro service, using the API Client ID and its associated Client ID Secret.

13. Click the List button in the jamfCPR app. You should now see a list of packages that your Jamf Pro service has, showing a status of different.

Note: To allow the migration process to go quickly for this blog post, I’m using dummy installer packages with a size of one kilobyte. The jamfCPR app should display the actual size of the installer packages in its application window when the installers are listed.

14. Select the packages you want to copy back from the downloaded copy of the installers to your Jamf Pro service.

15. Click the Replicate button.

You can monitor the replication process using the jamfCPR logs, which are available under the View menu using the Show Logs command.

16. Once replication is completed, the packages should appear as Availability Pending when you bring up information on the package in the Jamf Pro admin console.

You also won’t see any packages listed in the Cloud distribution point screen available via Settings: Server: Cloud distribution point.

This is normal and the package should already be available for installation. One way to verify that the packages are present in the JCDS2 distribution point is by downloading them from the JCDS2 distribution point. I have a post on how to do this available via the link below:

https://derflounder.wordpress.com/2024/02/24/using-the-jamf-pro-api-to-download-installer-packages-from-a-jcds2-distribution-point/

17. Install something from your Jamf Pro server and verify that installation is working correctly.

18. Verify in your policy logs that the installer is coming from an address which matches something similar to what’s shown below:

https://server_name_here.jamfcloud.com/jcds

Note: If you’re using a custom DNS name for your Jamf Cloud instance, it would appear similar to what’s shown below:

https://server_name_here.custom_domain.here/jcds

Post-migration:

After about an hour, the packages should appear listed in the Cloud distribution point screen available via Settings: Server: Cloud distribution point:

The Availability Pending message should also disappear when you view information about the installer package.

]]>
Der Flounder : Disabling login window clock display on macOS Sonoma https://derflounder.wordpress.com/2024/03/05/disabling-login-window-clock-display-on-macos-sonoma/ 2024-03-05T21:01:58+00:00 rtrouton One of the changes with macOS Sonoma is to the login window, where the username blanks and icons were made smaller and moved down to the bottom of the screen and a large clock display is enabled by default near the top of the login window.

For folks who don’t want the clock display, it’s possible to turn it off via System Settings. In the Lock Screen settings, set the Show large clock option to one of the following settings:

  • On Screen Saver and Lock Screen
  • On Lock Screen
  • Never

The Never setting will stop from being displayed on either the login window or as part of the screen saver.

For more information, please see below the jump.

For those who want a profile which can enforce the Never setting, please see below. It’s also available via the GitHub link below:

https://github.com/rtrouton/profiles/tree/main/DisableLoginWindowClock


<?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">
<dict>
<key>PayloadUUID</key>
<string>88268020-9123-40C7-8FEE-8505813ABCF2</string>
<key>PayloadType</key>
<string>Configuration</string>
<key>PayloadOrganization</key>
<string>Company Name</string>
<key>PayloadIdentifier</key>
<string>88268020-9123-40C7-8FEE-8505813ABCF2</string>
<key>PayloadDisplayName</key>
<string>Disable Login Window Clock</string>
<key>PayloadDescription</key>
<string />
<key>PayloadVersion</key>
<integer>1</integer>
<key>PayloadEnabled</key>
<true />
<key>PayloadRemovalDisallowed</key>
<true />
<key>PayloadScope</key>
<string>System</string>
<key>PayloadContent</key>
<array>
<dict>
<key>PayloadDisplayName</key>
<string>Custom Settings</string>
<key>PayloadIdentifier</key>
<string>3E1EB1DB-34AF-4E59-B797-02991E2F6048</string>
<key>PayloadOrganization</key>
<string>Company Name</string>
<key>PayloadType</key>
<string>com.apple.ManagedClient.preferences</string>
<key>PayloadUUID</key>
<string>3E1EB1DB-34AF-4E59-B797-02991E2F6048</string>
<key>PayloadVersion</key>
<integer>1</integer>
<key>PayloadContent</key>
<dict>
<key>com.apple.loginwindow</key>
<dict>
<key>Forced</key>
<array>
<dict>
<key>mcx_preference_settings</key>
<dict>
<key>UsesLargeDateTime</key>
<false />
</dict>
</dict>
</array>
</dict>
</dict>
</dict>
</array>
</dict>
</plist>

]]>
Der Flounder : Using the Jamf Pro API to download installer packages from a JCDS2 distribution point https://derflounder.wordpress.com/2024/02/24/using-the-jamf-pro-api-to-download-installer-packages-from-a-jcds2-distribution-point/ 2024-02-24T18:43:24+00:00 rtrouton As part of Jamf’s release of Jamf Cloud Distribution Service 2.0, Jamf has added the ability to communicate with a JCDS 2 distribution point via the Jamf Pro API:

https://learn.jamf.com/bundle/jamf-cloud-distribution-service-release-notes/page/Release_History.html

Among these new API capabilities is the ability to query a JCDS 2 DP for download links, for the installer packages stored in the distribution point.

https://developer.jamf.com/jamf-pro/reference/get_v1-jcds-files-filename

I’m interested in this because I’ve had an existing workflow for downloading installer packages from a non-JCDS AWS-hosted cloud distribution point, where I’ve been using AWS’s awscli command line tool to run a one-way synchronization process between the cloud distribution point in Amazon’s S3 service and a local directory stored on a Mac.

For those who want to use this new capability, I’ve written a script which uses the Jamf Pro Classic API and Jamf Pro API to get the list of installer packages on a Jamf Pro server, retrieve the associated download links and download the installer packages to a directory on my Mac. For more details, please see below the jump.

Pre-requisites:

If setting up a specific Jamf Pro user account for this purpose with limited rights, here are the required API privileges for the account on the Jamf Pro server:

Jamf Pro Server Objects:

  • Packages: Read
  • Jamf Content Distribution Server Files: Read

For authentication, the script can accept manual input or values stored in a ~/Library/Preferences/com.github.jamfpro-info.plist file. The plist file can be created by running the following commands and substituting your own values where appropriate:

To store the Jamf Pro URL in the plist file:


defaults write com.github.jamfpro-info jamfpro_url https://jamf.pro.server.goes.here:port_number_goes_here

view raw

gistfile1.txt

hosted with ❤ by GitHub

To store the account username in the plist file:


defaults write com.github.jamfpro-info jamfpro_user account_username_goes_here

view raw

gistfile1.txt

hosted with ❤ by GitHub

To store the account password in the plist file:


defaults write com.github.jamfpro-info jamfpro_password account_password_goes_here

view raw

gistfile1.txt

hosted with ❤ by GitHub

Usage: 

/path/to/Jamf_Pro_JCDS_Installer_Package_Download.sh

The script takes the following actions:

  1. Creates a download directory if none has been specified in the script.
  2. Uses the Jamf Pro Classic API to download the list of installer packages from the Jamf Pro server.
  3. Gets the Jamf Pro ID numbers for the individual installer packages.
  4. Uses the Jamf Pro Classic API to get the names of the individual installer packages.
  5. Checks to see if a file with a matching name exists in the download directory.
  6. If a file with a matching name exists in the download directory, display a message that the file exists in the download directory.
  7. If a file with a matching name does not exist in the download directory, use the Jamf Pro API to query the JCDS2 distribution point for the download URL of the installer package and download the installer package.

The script should provide output similar to this:


username@computername ~ % /path/to/Jamf_Pro_JCDS_Installer_Package_Download.sh
A location to store downloaded installer packages has not been specified.
Downloaded installer packages will be stored in /var/folders/vd/c27hl4p53j1_5cnv9ynpxp6m0000gn/T/tmp.EPeGBdNgVO.
Please enter your Jamf Pro server URL : https://jamf.pro.server.here
Please enter your Jamf Pro user account : username_goes_here
Please enter the password for the username_goes_here account:
Downloading Google_Chrome_121.0.6167.184.pkg to /var/folders/vd/c27hl4p53j1_5cnv9ynpxp6m0000gn/T/tmp.EPeGBdNgVO.
################################################################################################################################################# 100.0%
Downloading Google_Chrome_122.0.6261.57.pkg to /var/folders/vd/c27hl4p53j1_5cnv9ynpxp6m0000gn/T/tmp.EPeGBdNgVO.
################################################################################################################################################# 100.0%
Downloading Google_Chrome_122.0.6261.69.pkg to /var/folders/vd/c27hl4p53j1_5cnv9ynpxp6m0000gn/T/tmp.EPeGBdNgVO.
################################################################################################################################################# 100.0%
Microsoft_Defender_101.23122.0005.pkg is available in /var/folders/vd/c27hl4p53j1_5cnv9ynpxp6m0000gn/T/tmp.EPeGBdNgVO.
Microsoft_Edge_121.0.2277.128.pkg is available in /var/folders/vd/c27hl4p53j1_5cnv9ynpxp6m0000gn/T/tmp.EPeGBdNgVO.
Microsoft_Office_16.80.0.pkg is available in /var/folders/vd/c27hl4p53j1_5cnv9ynpxp6m0000gn/T/tmp.EPeGBdNgVO.
Microsoft_Office_16.81.2.pkg is available in /var/folders/vd/c27hl4p53j1_5cnv9ynpxp6m0000gn/T/tmp.EPeGBdNgVO.
Microsoft_Office_16.82.1.pkg is available in /var/folders/vd/c27hl4p53j1_5cnv9ynpxp6m0000gn/T/tmp.EPeGBdNgVO.
Microsoft_Office_16.82.pkg is available in /var/folders/vd/c27hl4p53j1_5cnv9ynpxp6m0000gn/T/tmp.EPeGBdNgVO.
Downloading Microsoft_OneDrive_24.020.0128.pkg to /var/folders/vd/c27hl4p53j1_5cnv9ynpxp6m0000gn/T/tmp.EPeGBdNgVO.
################################################################################################################################################# 100.0%
username@computername ~ %

view raw

gistfile1.txt

hosted with ❤ by GitHub

This script is available below and also from GitHub at the following location:

https://github.com/rtrouton/rtrouton_scripts/tree/main/rtrouton_scripts/Casper_Scripts/Jamf_Pro_JCDS_Installer_Package_Download/user_account_authentication


#!/bin/bash
# This script is designed to download installer packages from a JCDS 2 distribution point.
# As part of that, it uses the Jamf Pro API to identify the individual IDs of
# the installer packages stored on a Jamf Pro server then do the following:
#
# 1. Download the package information as XML
# 2. Identify the installer package name from downloaded XML
# 3. Get the download URL for the installer package
# 4. Save the installer package to a specified directory
# If setting up a specific user account with limited rights, here are the required API privileges
# for the account on the Jamf Pro server:
#
# Jamf Pro Server Objects:
#
# Packages: Read
# Jamf Content Distribution Server Files: Read
#
# Set exit error status
ERROR=0
# If you choose to specify a directory to save the downloaded installer packages into,
# please enter the complete directory path into the JCDSInstallerDownloadDirectory
# variable below.
JCDSInstallerDownloadDirectory=""
# If the JCDSInstallerDownloadDirectory isn't specified above, a directory will be
# created and the complete directory path displayed by the script.
if [[ -z "$JCDSInstallerDownloadDirectory" ]]; then
JCDSInstallerDownloadDirectory=$(mktemp -d)
echo "A location to store downloaded installer packages has not been specified."
echo "Downloaded installer packages will be stored in $JCDSInstallerDownloadDirectory."
fi
# If you choose to hardcode API information into the script, set one or more of the following values:
#
# The username for an account on the Jamf Pro server with sufficient API privileges
# The password for the account
# The Jamf Pro URL
# Set the Jamf Pro URL here if you want it hardcoded.
jamfpro_url=""
# Set the username here if you want it hardcoded.
jamfpro_user=""
# Set the password here if you want it hardcoded.
jamfpro_password=""
# If you do not want to hardcode API information into the script, you can also store
# these values in a ~/Library/Preferences/com.github.jamfpro-info.plist file.
#
# To create the file and set the values, run the following commands and substitute
# your own values where appropriate:
#
# To store the Jamf Pro URL in the plist file:
# defaults write com.github.jamfpro-info jamfpro_url https://jamf.pro.server.goes.here:port_number_goes_here
#
# To store the account username in the plist file:
# defaults write com.github.jamfpro-info jamfpro_user account_username_goes_here
#
# To store the account password in the plist file:
# defaults write com.github.jamfpro-info jamfpro_password account_password_goes_here
#
# If the com.github.jamfpro-info.plist file is available, the script will read in the
# relevant information from the plist file.
if [[ -f "$HOME/Library/Preferences/com.github.jamfpro-info.plist" ]]; then
if [[ -z "$jamfpro_url" ]]; then
jamfpro_url=$(defaults read $HOME/Library/Preferences/com.github.jamfpro-info jamfpro_url)
fi
if [[ -z "$jamfpro_user" ]]; then
jamfpro_user=$(defaults read $HOME/Library/Preferences/com.github.jamfpro-info jamfpro_user)
fi
if [[ -z "$jamfpro_password" ]]; then
jamfpro_password=$(defaults read $HOME/Library/Preferences/com.github.jamfpro-info jamfpro_password)
fi
fi
# If the Jamf Pro URL, the account username or the account password aren't available
# otherwise, you will be prompted to enter the requested URL or account credentials.
if [[ -z "$jamfpro_url" ]]; then
read -p "Please enter your Jamf Pro server URL : " jamfpro_url
fi
if [[ -z "$jamfpro_user" ]]; then
read -p "Please enter your Jamf Pro user account : " jamfpro_user
fi
if [[ -z "$jamfpro_password" ]]; then
read -p "Please enter the password for the $jamfpro_user account: " -s jamfpro_password
fi
echo ""
GetJamfProAPIToken() {
# This function uses Basic Authentication to get a new bearer token for API authentication.
# Use user account's username and password credentials with Basic Authorization to request a bearer token.
if [[ $(/usr/bin/sw_vers -productVersion | awk -F . '{print $1}') -lt 12 ]]; then
api_token=$(/usr/bin/curl -X POST –silent -u "${jamfpro_user}:${jamfpro_password}" "${jamfpro_url}/api/v1/auth/token" | python -c 'import sys, json; print json.load(sys.stdin)["token"]')
else
api_token=$(/usr/bin/curl -X POST –silent -u "${jamfpro_user}:${jamfpro_password}" "${jamfpro_url}/api/v1/auth/token" | plutil -extract token raw -)
fi
}
APITokenValidCheck() {
# Verify that API authentication is using a valid token by running an API command
# which displays the authorization details associated with the current API user.
# The API call will only return the HTTP status code.
api_authentication_check=$(/usr/bin/curl –write-out %{http_code} –silent –output /dev/null "${jamfpro_url}/api/v1/auth" –request GET –header "Authorization: Bearer ${api_token}")
}
CheckAndRenewAPIToken() {
# Verify that API authentication is using a valid token by running an API command
# which displays the authorization details associated with the current API user.
# The API call will only return the HTTP status code.
APITokenValidCheck
# If the api_authentication_check has a value of 200, that means that the current
# bearer token is valid and can be used to authenticate an API call.
if [[ ${api_authentication_check} == 200 ]]; then
# If the current bearer token is valid, it is used to connect to the keep-alive endpoint. This will
# trigger the issuing of a new bearer token and the invalidation of the previous one.
if [[ $(/usr/bin/sw_vers -productVersion | awk -F . '{print $1}') -lt 12 ]]; then
api_token=$(/usr/bin/curl "${jamfpro_url}/api/v1/auth/keep-alive" –silent –request POST –header "Authorization: Bearer ${api_token}" | python -c 'import sys, json; print json.load(sys.stdin)["token"]')
else
api_token=$(/usr/bin/curl "${jamfpro_url}/api/v1/auth/keep-alive" –silent –request POST –header "Authorization: Bearer ${api_token}" | plutil -extract token raw -)
fi
else
# If the current bearer token is not valid, this will trigger the issuing of a new bearer token
# using Basic Authentication.
GetJamfProAPIToken
fi
}
InvalidateToken() {
# Verify that API authentication is using a valid token by running an API command
# which displays the authorization details associated with the current API user.
# The API call will only return the HTTP status code.
APITokenValidCheck
# If the api_authentication_check has a value of 200, that means that the current
# bearer token is valid and can be used to authenticate an API call.
if [[ ${api_authentication_check} == 200 ]]; then
# If the current bearer token is valid, an API call is sent to invalidate the token.
authToken=$(/usr/bin/curl "${jamfpro_url}/api/v1/auth/invalidate-token" –silent –header "Authorization: Bearer ${api_token}" -X POST)
# Explicitly set value for the api_token variable to null.
api_token=""
fi
}
# Remove the trailing slash from the Jamf Pro URL if needed.
jamfpro_url=${jamfpro_url%%/}
# If configured to get one, get a Jamf Pro API Bearer Token
GetJamfProAPIToken
initializeJCDSInstallerDownloadDirectory ()
{
if [[ -z "$JCDSInstallerDownloadDirectory" ]]; then
JCDSInstallerDownloadDirectory=$(mktemp -d)
echo "A location to store downloaded installer packages has not been specified."
echo "Downloaded installer packages will be stored in $JCDSInstallerDownloadDirectory."
echo "$JCDSInstallerDownloadDirectory not found. Creating…"
mkdir -p $JCDSInstallerDownloadDirectory
if [[ $? -eq 0 ]]; then
echo "Successfully created $JCDSInstallerDownloadDirectory"
else
echo "Could not create $JCDSInstallerDownloadDirectory"
echo "Please make sure the parent directory is writable. Exiting…."
ERROR=1
fi
else
# Remove the trailing slash from the JCDSInstallerDownloadDirectory variable if needed.
JCDSInstallerDownloadDirectory=${JCDSInstallerDownloadDirectory%%/}
if [[ -d "$JCDSInstallerDownloadDirectory" ]] && [[ -z "$(ls -A "$JCDSInstallerDownloadDirectory")" ]]; then
echo "$JCDSInstallerDownloadDirectory exists but is empty. Using existing directory for downloading installer packages."
elif [[ -n "$JCDSInstallerDownloadDirectory" ]] && [[ ! -d "$JCDSInstallerDownloadDirectory" ]]; then
echo "$JCDSInstallerDownloadDirectory does not exist. Creating $JCDSInstallerDownloadDirectory for downloading installer packages."
mkdir -p $JCDSInstallerDownloadDirectory
if [[ $? -eq 0 ]]; then
echo "Successfully created new $JCDSInstallerDownloadDirectory"
else
echo "Could not create new $JCDSInstallerDownloadDirectory"
echo "Please make sure the parent directory is writable. Exiting…."
ERROR=1
fi
fi
fi
}
InstallerPackageDownloadURLRetrieval() {
# Replace spaces in filenames with %20, so that
# curl isn't trying to send a filename with spaces
# as part of an API command.
PackageNameSpacesSanitized=${PackageName// /%20}
# Retrieves a download URL for an installer package
if [[ $(/usr/bin/sw_vers -productVersion | awk -F . '{print $1}') -lt 12 ]]; then
InstallerPackageURI=$(/usr/bin/curl -s –header "Authorization: Bearer ${api_token}" "${jamfpro_url}/api/v1/jcds/files/${PackageNameSpacesSanitized}" -H "Accept: application/json" | python -c 'import sys, json; print json.load(sys.stdin)["uri"]')
else
InstallerPackageURI=$(/usr/bin/curl -s –header "Authorization: Bearer ${api_token}" "${jamfpro_url}/api/v1/jcds/files/${PackageNameSpacesSanitized}" -H "Accept: application/json" | plutil -extract uri raw -)
fi
}
# The following function downloads individual Jamf Pro policy as XML data
# then mines the policy data for the relevant information, which are the
# download URLs for the packages stored in the JCDS distribution point.
#
# Once the download URLs are identified, the installer packages are then
# downloaded to the specified download directory. If there are installer
# packages already in the download directory which have the same name
# as an installer package in the JCDS distribution point, download of
# the installer package with the matching name is skipped.
DownloadInstallerPackages(){
local InstallerPackageID="$1"
if [[ -n "$InstallerPackageID" ]]; then
CheckAndRenewAPIToken
local DownloadedXMLData=$(/usr/bin/curl -s –header "Authorization: Bearer ${api_token}" -H "Accept: application/xml" "${jamfpro_url}/JSSResource/packages/id/$InstallerPackageID")
local PackageName=$( echo "$DownloadedXMLData" | xmllint –xpath '/package/filename/text()' – 2>/dev/null)
# Download installer packages to the download directory
if [[ -n "$PackageName" ]]; then
InstallerPackageCheck=$(ls -a "$JCDSInstallerDownloadDirectory" | grep "$PackageName")
# Only download installer packages that haven't already been downloaded.
if [[ -z "$InstallerPackageCheck" ]]; then
echo "Downloading $PackageName to $JCDSInstallerDownloadDirectory."
InstallerPackageDownloadURLRetrieval
curl –progress-bar ${InstallerPackageURI} -X GET –output "${JCDSInstallerDownloadDirectory}"/"${PackageName}"
else
echo "$PackageName is available in $JCDSInstallerDownloadDirectory."
fi
fi
fi
}
initializeJCDSInstallerDownloadDirectory
if [[ $ERROR -eq 0 ]]; then
# Download all Jamf Pro installer package ID numbers
CheckAndRenewAPIToken
InstallerPackageIDList=$(/usr/bin/curl -s –header "Authorization: Bearer ${api_token}" -H "Accept: application/xml" "${jamfpro_url}/JSSResource/packages" | xmllint –xpath '//id' – 2>/dev/null)
InstallerPackageIDs=$(echo "$InstallerPackageIDList" | grep -Eo "[0-9]+")
for anID in ${InstallerPackageIDs}; do
# Download installer packages from the JCDS distribution point.
DownloadInstallerPackages $anID
done
fi
exit $ERROR

view raw

gistfile1.txt

hosted with ❤ by GitHub

A version of this script which uses Jamf’s API client authentication instead of a Jamf Pro user account is available from GitHub at the following location:

https://github.com/rtrouton/rtrouton_scripts/tree/main/rtrouton_scripts/Casper_Scripts/Jamf_Pro_JCDS_Installer_Package_Download/API_client_authentication

Both scripts can be accessed via the following link:

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

]]>
Der Flounder : Building distribution packages using AutoPkg https://derflounder.wordpress.com/2024/02/04/building-distribution-packages-using-autopkg/ 2024-02-04T20:39:43+00:00 rtrouton I’ve been thinking about the issue of building installer packages using AutoPkg which are ready for installation using MDM commands. Installing an installer package via MDM command requires packages to have the following attributes:

  1. Signed with an Apple Developer ID Installer certificate
  2. Be a distribution installer package

For criteria #2, this references the fact that there are two kinds of modern installer packages for macOS:

  • Component packages: these are the standard type of installer package, which contain an archive of files to install and the information on where the files should be installed.
  • Distribution packages: These packages can contain one or more component packages, and may also include additional resources to customize and control the user interface shown in the Installer application.

By default, AutoPkg will build component packages using the PkgCreator processor or the AppPkgCreator processor. But there is a relatively straightforward way to create a a distribution package while using an existing component package as a source, using the productbuild command. To create a distribution installer package from an existing component installer package, you would use a command similar to the one shown below:


/usr/bin/productbuild –package /path/to/package_being_converted_to_distribution.pkg /path/to/new_distribution_package.pkg

view raw

gistfile1.txt

hosted with ❤ by GitHub

Note: If using a signed component installer package as a source, the resulting new distribution package will not be signed. If needed, you will need to sign the distribution package following its creation.

For those who want to create distribution packages as part of an AutoPkg workflow, I’ve written a DistributionPackageCreator AutoPkg processor which is designed to perform the following tasks:

  1. Rename the existing AutoPkg-generated component package.
  2. Create a new distribution package from the AutoPkg-generated component package.
  3. Set the newly-created distribution package to have the original name of the AutoPkg-generated component package.

For more details, please see below the jump.

The DistributionPackageCreator processor is shown below, as well as being available via the following link:

https://github.com/rtrouton/AutoPkg_Processors/tree/main/DistributionPackageCreator


#!/usr/local/autopkg/python
#
# Copyright 2010 Per Olofsson
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import plistlib
import subprocess
import os
from autopkglib import Processor, ProcessorError
__all__ = ["DistributionPackageCreator"]
class DistributionPackageCreator(Processor):
description = "Creates a distribution package from an existing component package using ProductBuild."
input_variables = {
"pkg_path": {
"required": True,
"description": "Path to the component package used to create the distribution package",
},
}
output_variables = {
"pkg_path": {"description": "Path to the distribution package."}
}
__doc__ = description
def main(self):
# Rename component package so that we can retain the original name for the distribution package.
pkg_dir = os.path.dirname(self.env["pkg_path"])
pkg_base_name = os.path.basename(self.env["pkg_path"])
(pkg_name_no_extension, pkg_extension) = os.path.splitext(pkg_base_name)
component_pkg_path = os.path.join(
pkg_dir, pkg_name_no_extension + "-component" + pkg_extension
)
os.rename(self.env["pkg_path"], component_pkg_path)
command_line_list = [
"/usr/bin/productbuild",
"–package",
component_pkg_path,
self.env["pkg_path"],
]
print(command_line_list)
# print command_line_list
subprocess.call(command_line_list)
if __name__ == "__main__":
processor = DistributionPackageCreator()
processor.execute_shell()


Update – 2-5-2024: It turns out that both myself and @davidbpirie wrote practically identical AutoPkg processors. His processor is FlatToDistPkg (written in 2022) and it is available in his repo:

https://github.com/autopkg/davidbpirie-recipes/tree/main/SharedProcessors


When included in an AutoPkg recipe, the DistributionPackageCreator processor will locate AutoPkg-generated component packages by using the pkg_path variable and do the following:

  1. Rename the AutoPkg-generated component package from /path/to/package_name_here.pkg to /path/to/package_name_here-component.pkg
  2. Create a new distribution package from the AutoPkg-generated component package.
  3. Save the distribution package as /path/to/package_name_here.pkg, so that the name matches the original package.

Note: Setting the distribution package’s name to match the original component package’s name allows AutoPkg to continue to work with the distribution installer package.

To assist folks who want to use this processor, but don’t want to rewrite their existing .pkg recipes, I’ve written an example recipe to assist with this: the .distpkg recipe.

The .distpkg recipe uses the DistributionPackageCreator processor and is designed to be placed in the AutoPkg workflow between a .pkg recipe and whatever else came next. In this case, the .pkg recipe would be a parent recipe for the .distpkg recipe. In turn, the .distpkg recipe would be used as the parent recipe for whatever came next in the workflow.

A good example would be if you wanted to create a signed distribution package. In that case, you could combine a .pkg recipe, a .distpkg recipe and a .sign recipe into the same workflow to produce a signed distribution package, which should meet all the necessary requirements to install the package via an MDM command.

For those who want to use .distpkg recipes, there is an example recipe available via the link below:

https://github.com/autopkg/rtrouton-recipes/blob/master/SharedProcessors/Example.distpkg.recipe

https://gist.github.com/rtrouton/f8f2433843d0b5a7880439ad0fce4298

If you want to use the DistributionPackageCreator processor hosted from my AutoPkg recipe repo, first verify that AutoPkg is installed on the Mac you’re using. Once verified, run the following command:


autopkg repo-add rtrouton-recipes

view raw

gistfile1.txt

hosted with ❤ by GitHub

]]>
Der Flounder : Using AutoPkg to build a Cisco Secure Client installer https://derflounder.wordpress.com/2024/01/27/using-autopkg-to-build-a-cisco-secure-client-installer/ 2024-01-27T12:42:13+00:00 rtrouton Fraser Hess recently posted about automating the creation of Cisco Secure Client installers. Similar to my earlier post on using AutoPkg to build a Cisco AnyConnect installer, it’s possible to replicate this packaging workflow, including generating an installer choices XML file, using AutoPkg. For more details, please see below the jump.

In this example, there are going to be multiple AutoPkg recipes and support files referenced:

  • CiscoSecureClient.download.recipe – Download recipe for the vendor-supplied Cisco Secure Client disk image with the vendor-supplied installer package stored inside.
  • CiscoSecureClient.pkg.recipe – Package recipe for Cisco Secure Client, which generates an installer choices XML file and wraps both the installer choices XML file and the vendor-supplied installer package inside a separate installer package generated by AutoPkg
  • Example.xml – Sample VPN profile for Cisco Secure Client’s VPN module
  • CiscoSecureClient package recipe override – This is the AutoPkg recipe override where you’re defining how the installer choices file is configured and other information being supplied to the Cisco Secure Client installer by the AutoPkg package creation process.

Important information:

A. The recipes as written assume the following:

  • You’re using the Cisco Secure Client Umbrella module.
  • You’re adding the necessary configuration information for the Cisco Secure Client Umbrella module to the AutoPkg recipe override.
  • You may be using the Cisco Secure Client VPN module.

B. You absolutely must create an AutoPkg override to work with these recipes. The download location, configuration for the installer choices XML file and other settings are not included in the AutoPkg recipes themselves and must be defined in the override.

C. The Cisco Secure Client disk image does not have a set address for download, so you will need to do one of the following:

  • Download the disk image from Cisco and host it yourself somewhere.
  • Change the download URL in the AutoPkg recipe override to match wherever you can currently download the Cisco Secure Client disk image from.

D. To configure the installer choices XML file, you must designate what modules you want to include using ones and zeros in the AutoPkg override. By default, the .pkg recipe is configured to install all modules:


<key>CHOICE_VPN</key>
<string>1</string>
<key>CHOICE_WEBSECURITY</key>
<string>1</string>
<key>CHOICE_FIREAMP</key>
<string>1</string>
<key>CHOICE_DART</key>
<string>1</string>
<key>CHOICE_DUO</key>
<string>1</string>
<key>CHOICE_POSTURE</key>
<string>1</string>
<key>CHOICE_ISEPOSTURE</key>
<string>1</string>
<key>CHOICE_NVM</key>
<string>1</string>
<key>CHOICE_THOUSANDEYES</key>
<string>1</string>
<key>CHOICE_UMBRELLA</key>
<string>1</string>
<key>CHOICE_ZEROTRUST</key>
<string>1</string>

view raw

gistfile1.txt

hosted with ❤ by GitHub

To change this, change one to zero for the modules you don’t want to install. For example, the configuration below will configure the Secure Client installer to only install the Secure Client Umbrella module:


<key>CHOICE_VPN</key>
<string>0</string>
<key>CHOICE_WEBSECURITY</key>
<string>0</string>
<key>CHOICE_FIREAMP</key>
<string>0</string>
<key>CHOICE_DART</key>
<string>0</string>
<key>CHOICE_DUO</key>
<string>0</string>
<key>CHOICE_POSTURE</key>
<string>0</string>
<key>CHOICE_ISEPOSTURE</key>
<string>0</string>
<key>CHOICE_NVM</key>
<string>0</string>
<key>CHOICE_THOUSANDEYES</key>
<string>0</string>
<key>CHOICE_UMBRELLA</key>
<string>1</string>
<key>CHOICE_ZEROTRUST</key>
<string>0</string>

view raw

gistfile1.txt

hosted with ❤ by GitHub

E. If you do not want to have the VPN module installed or enabled, you will need to set the CHOICE_VPN and DISABLE_VPN settings in the recipe override. Please see below for an example:


<key>CHOICE_VPN</key>
<string>0</string>
<key>CHOICE_WEBSECURITY</key>
<string>0</string>
<key>CONTENT_XML</key>
<string>Put_escaped_XML_profile_text_into_AutoPkg_recipe_override</string>
<key>DISABLE_CUSTOMER_EXPERIENCE_FEEDBACK</key>
<string>false</string>
<key>DISABLE_VPN</key>
<string>true</string>

view raw

gistfile1.txt

hosted with ❤ by GitHub

In this example, the CHOICE_VPN setting is set to zero and the DISABLE_VPN setting is set to true.

F. These recipes allow you to hide the Cisco-provided Secure Client installers, so that your users will not be able to see them in the Finder. Like the installer choices selection, this can be set using ones and zeros in the AutoPkg override.

To hide, set the HIDE_UNINSTALLERS setting to one:


<key>HIDE_UNINSTALLERS</key>
<string>1</string>

view raw

gistfile1.txt

hosted with ❤ by GitHub

To not hide the uninstallers, set the HIDE_UNINSTALLERS setting to zero:


<key>HIDE_UNINSTALLERS</key>
<string>0</string>

view raw

gistfile1.txt

hosted with ❤ by GitHub

G. It’s possible to disable the customer feedback functionality through the installer. To set this to be disabled, set the DISABLE_CUSTOMER_EXPERIENCE_FEEDBACK setting to true.


<key>DISABLE_CUSTOMER_EXPERIENCE_FEEDBACK</key>
<string>true</string>

view raw

gistfile1.txt

hosted with ❤ by GitHub

To leave the customer feedback functionality enabled, set the DISABLE_CUSTOMER_EXPERIENCE_FEEDBACK setting to false.


<key>DISABLE_CUSTOMER_EXPERIENCE_FEEDBACK</key>
<string>false</string>

view raw

gistfile1.txt

hosted with ❤ by GitHub

Please see below for the example .download and .pkg recipes, example VPN XML file and example .pkg recipe override:

Download recipe:


<?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 a Cisco Secure Client package from a specified URL.</string>
<key>Identifier</key>
<string>com.company.download.CiscoSecureClient</string>
<key>Input</key>
<dict>
<key>NAME</key>
<string>Cisco Secure Client</string>
<key>VENDOR</key>
<string>Cisco</string>
<key>DOWNLOAD_URL</key>
<string>Put_download_URL_into_AutoPkg_recipe_override</string>
</dict>
<key>MinimumVersion</key>
<string>1.0.0</string>
<key>Process</key>
<array>
<dict>
<key>Arguments</key>
<dict>
<key>predicate</key>
<string>DOWNLOAD_URL == "Put_download_URL_into_AutoPkg_recipe_override"</string>
</dict>
<key>Processor</key>
<string>StopProcessingIf</string>
</dict>
<dict>
<key>Processor</key>
<string>URLDownloader</string>
<key>Arguments</key>
<dict>
<key>url</key>
<string>%DOWNLOAD_URL%</string>
<key>request_headers</key>
<dict>
<key>user-agent</key>
<string>%DOWNLOAD_USERAGENT%</string>
<key>referer</key>
<string>%DOWNLOAD_REFERER%</string>
</dict>
</dict>
</dict>
<dict>
<key>Processor</key>
<string>FlatPkgUnpacker</string>
<key>Arguments</key>
<dict>
<key>flat_pkg_path</key>
<string>%pathname%/Cisco Secure Client.pkg</string>
<key>destination_path</key>
<string>%RECIPE_CACHE_DIR%/%VENDOR%/unpack</string>
<key>purge_destination</key>
<true />
</dict>
</dict>
<dict>
<key>Processor</key>
<string>PkgPayloadUnpacker</string>
<key>Arguments</key>
<dict>
<key>pkg_payload_path</key>
<string>%RECIPE_CACHE_DIR%/%VENDOR%/unpack/vpn_module.pkg/Payload</string>
<key>destination_path</key>
<string>%RECIPE_CACHE_DIR%/%VENDOR%/vpn_module_payload</string>
<key>purge_destination</key>
<true />
</dict>
</dict>
<dict>
<key>Processor</key>
<string>Versioner</string>
<key>Arguments</key>
<dict>
<key>input_plist_path</key>
<string>%RECIPE_CACHE_DIR%/%VENDOR%/vpn_module_payload/Applications/Cisco/Cisco Secure Client.app/Contents/Info.plist</string>
<key>plist_version_key</key>
<string>CFBundleShortVersionString</string>
</dict>
</dict>
<dict>
<key>Processor</key>
<string>EndOfCheckPhase</string>
</dict>
</array>
</dict>
</plist>

view raw

gistfile1.txt

hosted with ❤ by GitHub

Package recipe:


<?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>Comments</key>
<string>Based on https://github.com/autopkg/grahampugh-recipes/blob/master/CiscoSecureClient/CiscoSecureClient.pkg.recipe
Wraps the vendor package so that it can be run with Choice Changes XML.
Requires a configuration xml profile (VPN_XML) which should be added to the RECIPE_OVERRIDE_DIR before running the recipe. An example is provided in the recipe repo folder, named example.xml.
If you need more packages installing, you should make a copy of this recipe and edit the ChoicesXMLGenerator choices list. This isn't overridable. Don't forget to change the Identifier if you do this.</string>
<key>Description</key>
<string>Creates a Cisco Secure Client package with all modules enabled.</string>
<key>Identifier</key>
<string>com.company.pkg.CiscoSecureClient-AllModulesEnabled</string>
<key>ParentRecipe</key>
<string>com.company.download.CiscoSecureClient</string>
<key>Input</key>
<dict>
<key>NAME</key>
<string>Cisco Secure Client</string>
<key>VPN_XML</key>
<string>example.xml</string>
<key>CONTENT_XML</key>
<string>Put_escaped_XML_profile_text_into_AutoPkg_recipe_override</string>
<key>UMBRELLA_ORGANIZATION_ID</key>
<string>Put_text_into_AutoPkg_recipe_override</string>
<key>UMBRELLA_FINGERPRINT</key>
<string>Put_text_into_AutoPkg_recipe_override</string>
<key>UMBRELLA_USER_ID</key>
<string>Put_text_into_AutoPkg_recipe_override</string>
<key>CHOICE_VPN</key>
<string>1</string>
<key>CHOICE_WEBSECURITY</key>
<string>1</string>
<key>CHOICE_FIREAMP</key>
<string>1</string>
<key>CHOICE_DART</key>
<string>1</string>
<key>CHOICE_DUO</key>
<string>1</string>
<key>CHOICE_POSTURE</key>
<string>1</string>
<key>CHOICE_ISEPOSTURE</key>
<string>1</string>
<key>CHOICE_NVM</key>
<string>1</string>
<key>CHOICE_THOUSANDEYES</key>
<string>1</string>
<key>CHOICE_UMBRELLA</key>
<string>1</string>
<key>CHOICE_ZEROTRUST</key>
<string>1</string>
<key>DISABLE_VPN</key>
<string>false</string>
<key>DISABLE_CUSTOMER_EXPERIENCE_FEEDBACK</key>
<string>false</string>
<key>HIDE_UNINSTALLERS</key>
<string>1</string>
<key>VENDOR</key>
<string>Cisco</string>
<key>SOFTWARETITLE1</key>
<string>Secure</string>
<key>SOFTWARETITLE2</key>
<string>Client</string>
<key>SOFTWARETITLE3</key>
<string>All_Modules_Enabled</string>
<key>PKGID</key>
<string>com.company.cisco.SecureClient</string>
</dict>
<key>MinimumVersion</key>
<string>1.0.0</string>
<key>Process</key>
<array>
<dict>
<key>Arguments</key>
<dict>
<key>pkgdirs</key>
<dict>
<key>Profiles</key>
<string>0755</string>
<key>Profiles/ampenabler</key>
<string>0755</string>
<key>Profiles/feedback</key>
<string>0755</string>
<key>Profiles/iseposture</key>
<string>0755</string>
<key>Profiles/nvm</key>
<string>0755</string>
<key>Profiles/umbrella</key>
<string>0755</string>
<key>Profiles/vpn</key>
<string>0755</string>
<key>Profiles/websecurity</key>
<string>0755</string>
</dict>
<key>pkgroot</key>
<string>%RECIPE_CACHE_DIR%/Scripts</string>
</dict>
<key>Processor</key>
<string>PkgRootCreator</string>
</dict>
<dict>
<key>Arguments</key>
<dict>
<key>pkgdirs</key>
<dict />
<key>pkgroot</key>
<string>%RECIPE_CACHE_DIR%/pkgroot</string>
</dict>
<key>Processor</key>
<string>PkgRootCreator</string>
</dict>
<dict>
<key>Arguments</key>
<dict>
<key>pkg_path</key>
<string>%RECIPE_CACHE_DIR%/Scripts/CiscoSecureClient.pkg</string>
<key>source_pkg</key>
<string>%pathname%/Cisco Secure Client.pkg</string>
</dict>
<key>Processor</key>
<string>PkgCopier</string>
</dict>
<dict>
<key>Arguments</key>
<dict>
<key>file_content</key>
<string>%CONTENT_XML%</string>
<key>file_mode</key>
<string>0644</string>
<key>file_path</key>
<string>%RECIPE_CACHE_DIR%/Scripts/Profiles/vpn/%VPN_XML%</string>
</dict>
<key>Processor</key>
<string>FileCreator</string>
</dict>
<dict>
<key>Arguments</key>
<dict>
<key>file_content</key>
<string>{
"organizationId" : "%UMBRELLA_ORGANIZATION_ID%",
"fingerprint" : "%UMBRELLA_FINGERPRINT%",
"userId" : "%UMBRELLA_USER_ID%"
}</string>
<key>file_mode</key>
<string>0644</string>
<key>file_path</key>
<string>%RECIPE_CACHE_DIR%/Scripts/Profiles/umbrella/OrgInfo.json</string>
</dict>
<key>Processor</key>
<string>FileCreator</string>
</dict>
<dict>
<key>Arguments</key>
<dict>
<key>file_content</key>
<string>&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"&gt;
&lt;plist version="1.0"&gt;
&lt;array&gt;
&lt;dict&gt;
&lt;key&gt;attributeSetting&lt;/key&gt;
&lt;integer&gt;%CHOICE_VPN%&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;choice_anyconnect_vpn&lt;/string&gt;
&lt;/dict&gt;
&lt;dict&gt;
&lt;key&gt;attributeSetting&lt;/key&gt;
&lt;integer&gt;%CHOICE_WEBSECURITY%&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;choice_websecurity&lt;/string&gt;
&lt;/dict&gt;
&lt;dict&gt;
&lt;key&gt;attributeSetting&lt;/key&gt;
&lt;integer&gt;%CHOICE_FIREAMP%&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;choice_fireamp&lt;/string&gt;
&lt;/dict&gt;
&lt;dict&gt;
&lt;key&gt;attributeSetting&lt;/key&gt;
&lt;integer&gt;%CHOICE_DART%&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;choice_dart&lt;/string&gt;
&lt;/dict&gt;
&lt;dict&gt;
&lt;key&gt;attributeSetting&lt;/key&gt;
&lt;integer&gt;%CHOICE_DUO%&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;choice_duo&lt;/string&gt;
&lt;/dict&gt;
&lt;dict&gt;
&lt;key&gt;attributeSetting&lt;/key&gt;
&lt;integer&gt;%CHOICE_POSTURE%&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;choice_secure_firewall_posture&lt;/string&gt;
&lt;/dict&gt;
&lt;dict&gt;
&lt;key&gt;attributeSetting&lt;/key&gt;
&lt;integer&gt;%CHOICE_ISEPOSTURE%&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;choice_iseposture&lt;/string&gt;
&lt;/dict&gt;
&lt;dict&gt;
&lt;key&gt;attributeSetting&lt;/key&gt;
&lt;integer&gt;%CHOICE_NVM%&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;choice_nvm&lt;/string&gt;
&lt;/dict&gt;
&lt;dict&gt;
&lt;key&gt;attributeSetting&lt;/key&gt;
&lt;integer&gt;%CHOICE_THOUSANDEYES%&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;choice_thousandeyes&lt;/string&gt;
&lt;/dict&gt;
&lt;dict&gt;
&lt;key&gt;attributeSetting&lt;/key&gt;
&lt;integer&gt;%CHOICE_UMBRELLA%&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;choice_secure_umbrella&lt;/string&gt;
&lt;/dict&gt;
&lt;dict&gt;
&lt;key&gt;attributeSetting&lt;/key&gt;
&lt;integer&gt;%CHOICE_ZEROTRUST%&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;choice_zta&lt;/string&gt;
&lt;/dict&gt;
&lt;/array&gt;
&lt;/plist&gt;</string>
<key>file_mode</key>
<string>0755</string>
<key>file_path</key>
<string>%RECIPE_CACHE_DIR%/Scripts/InstallerChoices.xml</string>
</dict>
<key>Processor</key>
<string>FileCreator</string>
</dict>
<dict>
<key>Arguments</key>
<dict>
<key>file_content</key>
<string>&lt;!– Optional SecureClient installer settings are provided below. Configure the setting(s) as "true" (default "false" or commented out) to perform optional action(s) at install time. –&gt;
&lt;Transforms&gt;
&lt;DisableVPN&gt;%DISABLE_VPN%&lt;/DisableVPN&gt;
&lt;DisableCustomerExperienceFeedback&gt;%DISABLE_CUSTOMER_EXPERIENCE_FEEDBACK%&lt;/DisableCustomerExperienceFeedback&gt;
&lt;/Transforms&gt;</string>
<key>file_mode</key>
<string>0755</string>
<key>file_path</key>
<string>%RECIPE_CACHE_DIR%/Scripts/Profiles/ACTransforms.xml</string>
</dict>
<key>Processor</key>
<string>FileCreator</string>
</dict>
<dict>
<key>Arguments</key>
<dict>
<key>file_content</key>
<string>#!/bin/bash
packagePath="${0%/*}/CiscoSecureClient.pkg"
choicesXML="${0%/*}/InstallerChoices.xml"
# Set value in AutoPkg override, default is 1 for "true"
hideUninstallers="%HIDE_UNINSTALLERS%"
# Array of uninstall application bundles on the installer's target volume
if [[ -n $3 ]]; then
targetVolume=$3
else
targetVolume=""
fi
uninstallAppBundlePaths=(
"${targetVolume}/Applications/Cisco/Uninstall Cisco Secure Client – DART.app"
"${targetVolume}/Applications/Cisco/Uninstall Cisco Secure Client.app"
)
exitCode=0
# Run embedded installer with the Choice Changes XML
if [[ -r "${packagePath}" &amp;&amp; -r "${choicesXML}" ]]; then
if ! /usr/sbin/installer -applyChoiceChangesXML "${choicesXML}" -pkg "${packagePath}" -target "$3"; then
echo "Installation of package \"${packagePath}\" failed."
exitCode=1
fi
else
echo "Package \"${packagePath}\" not found."
exitCode=1
fi
# Hide uninstallers
# Override will specify 1 as "true"
# Set to another value to specify "false"
# If variable is unset/null or still contains an AutoPkg variable reference, treat as "true"
if [[ "${hideUninstallers}" -eq 1 || \
-z "${hideUninstallers}" || \
"${hideUninstallers}" == "%""HIDE_UNINSTALLERS""%" ]]; then
for uninstallAppBundlePath in "${uninstallAppBundlePaths[@]}"
do
if [[ -d "${uninstallAppBundlePath}" ]]; then
if /usr/bin/chflags hidden "${uninstallAppBundlePath}"; then
echo "Uninstaller app bundle \"${uninstallAppBundlePath}\" was hidden successfully."
else
echo "Uninstaller app bundle \"${uninstallAppBundlePath}\" was not hidden successfully."
exitCode=1
fi
else
echo "Uninstaller app bundle \"${uninstallAppBundlePath}\" was not found and cannot be hidden."
fi
done
else
echo "Uninstallers will not be hidden."
fi
exit "${exitCode}"</string>
<key>file_mode</key>
<string>0755</string>
<key>file_path</key>
<string>%RECIPE_CACHE_DIR%/Scripts/postinstall</string>
</dict>
<key>Processor</key>
<string>FileCreator</string>
</dict>
<dict>
<key>Arguments</key>
<dict>
<key>pkg_request</key>
<dict>
<key>chown</key>
<array />
<key>id</key>
<string>%PKGID%.%VENDOR%%SOFTWARETITLE1%%SOFTWARETITLE2%</string>
<key>pkgname</key>
<string>%VENDOR%_%SOFTWARETITLE1%_%SOFTWARETITLE2%_%SOFTWARETITLE3%_%version%</string>
<key>pkgroot</key>
<string>%RECIPE_CACHE_DIR%/pkgroot</string>
<key>pkgtype</key>
<string>flat</string>
<key>scripts</key>
<string>%RECIPE_CACHE_DIR%/Scripts</string>
<key>version</key>
<string>%version%</string>
</dict>
</dict>
<key>Processor</key>
<string>PkgCreator</string>
</dict>
<dict>
<key>Arguments</key>
<dict>
<key>path_list</key>
<array>
<string>%RECIPE_CACHE_DIR%/%VENDOR%</string>
<string>%RECIPE_CACHE_DIR%/pkgroot</string>
</array>
</dict>
<key>Processor</key>
<string>PathDeleter</string>
</dict>
</array>
</dict>
</plist>

Example VPN XML file:


<?xml version="1.0" encoding="UTF-8"?>
<AnyConnectProfile xmlns="http://schemas.xmlsoap.org/encoding/&quot;
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance&quot; xsi:schemaLocation="http://schemas.xmlsoap.org/encoding/ AnyConnectProfile.xsd">
<ClientInitialization>
<UseStartBeforeLogon UserControllable="true">false</UseStartBeforeLogon>
<AutomaticCertSelection UserControllable="false">true</AutomaticCertSelection>
<ShowPreConnectMessage>false</ShowPreConnectMessage>
<CertificateStore>All</CertificateStore>
<CertificateStoreOverride>true</CertificateStoreOverride>
<ProxySettings>Native</ProxySettings>
<AllowLocalProxyConnections>true</AllowLocalProxyConnections>
<AuthenticationTimeout>12</AuthenticationTimeout>
<AutoConnectOnStart UserControllable="true">false</AutoConnectOnStart>
<MinimizeOnConnect UserControllable="true">true</MinimizeOnConnect>
<LocalLanAccess UserControllable="true">true</LocalLanAccess>
<DisableCaptivePortalDetection UserControllable="false">false</DisableCaptivePortalDetection>
<ClearSmartcardPin UserControllable="false">true</ClearSmartcardPin>
<IPProtocolSupport>IPv4,IPv6</IPProtocolSupport>
<AutoReconnect UserControllable="false">true
<AutoReconnectBehavior UserControllable="false">DisconnectOnSuspend</AutoReconnectBehavior>
</AutoReconnect>
<AutoUpdate UserControllable="false">true</AutoUpdate>
<RSASecurIDIntegration UserControllable="false">Automatic</RSASecurIDIntegration>
<WindowsLogonEnforcement>SingleLocalLogon</WindowsLogonEnforcement>
<WindowsVPNEstablishment>LocalUsersOnly</WindowsVPNEstablishment>
<AutomaticVPNPolicy>false</AutomaticVPNPolicy>
<PPPExclusion UserControllable="false">Disable
<PPPExclusionServerIP UserControllable="false"></PPPExclusionServerIP>
</PPPExclusion>
<EnableScripting UserControllable="false">false</EnableScripting>
<EnableAutomaticServerSelection UserControllable="true">false
<AutoServerSelectionImprovement>20</AutoServerSelectionImprovement>
<AutoServerSelectionSuspendTime>4</AutoServerSelectionSuspendTime>
</EnableAutomaticServerSelection>
<RetainVpnOnLogoff>false</RetainVpnOnLogoff>
<AllowManualHostInput>true</AllowManualHostInput>
</ClientInitialization>
<ServerList>
<HostEntry>
<HostName>example.com/</HostName>
<HostAddress>example.com</HostAddress>
<UserGroup>example</UserGroup>
</HostEntry>
</ServerList>
</AnyConnectProfile>

view raw

example.xml

hosted with ❤ by GitHub

Example .pkg recipe override:


<?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.CiscoSecureClient-AllModulesEnabled</string>
<key>Input</key>
<dict>
<key>CHOICE_DART</key>
<string>1</string>
<key>CHOICE_DUO</key>
<string>1</string>
<key>CHOICE_FIREAMP</key>
<string>1</string>
<key>CHOICE_ISEPOSTURE</key>
<string>1</string>
<key>CHOICE_NVM</key>
<string>1</string>
<key>CHOICE_POSTURE</key>
<string>1</string>
<key>CHOICE_THOUSANDEYES</key>
<string>1</string>
<key>CHOICE_UMBRELLA</key>
<string>1</string>
<key>CHOICE_VPN</key>
<string>1</string>
<key>CHOICE_WEBSECURITY</key>
<string>1</string>
<key>CHOICE_ZEROTRUST</key>
<string>1</string>
<key>CONTENT_XML</key>
<string>&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
&lt;AnyConnectProfile xmlns=&quot;http://schemas.xmlsoap.org/encoding/&quot;
xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot; xsi:schemaLocation=&quot;http://schemas.xmlsoap.org/encoding/ AnyConnectProfile.xsd&quot;&gt;
&lt;ClientInitialization&gt;
&lt;UseStartBeforeLogon UserControllable=&quot;true&quot;&gt;false&lt;/UseStartBeforeLogon&gt;
&lt;AutomaticCertSelection UserControllable=&quot;false&quot;&gt;true&lt;/AutomaticCertSelection&gt;
&lt;ShowPreConnectMessage&gt;false&lt;/ShowPreConnectMessage&gt;
&lt;CertificateStore&gt;All&lt;/CertificateStore&gt;
&lt;CertificateStoreOverride&gt;true&lt;/CertificateStoreOverride&gt;
&lt;ProxySettings&gt;Native&lt;/ProxySettings&gt;
&lt;AllowLocalProxyConnections&gt;true&lt;/AllowLocalProxyConnections&gt;
&lt;AuthenticationTimeout&gt;12&lt;/AuthenticationTimeout&gt;
&lt;AutoConnectOnStart UserControllable=&quot;true&quot;&gt;false&lt;/AutoConnectOnStart&gt;
&lt;MinimizeOnConnect UserControllable=&quot;true&quot;&gt;true&lt;/MinimizeOnConnect&gt;
&lt;LocalLanAccess UserControllable=&quot;true&quot;&gt;true&lt;/LocalLanAccess&gt;
&lt;DisableCaptivePortalDetection UserControllable=&quot;false&quot;&gt;false&lt;/DisableCaptivePortalDetection&gt;
&lt;ClearSmartcardPin UserControllable=&quot;false&quot;&gt;true&lt;/ClearSmartcardPin&gt;
&lt;IPProtocolSupport&gt;IPv4,IPv6&lt;/IPProtocolSupport&gt;
&lt;AutoReconnect UserControllable=&quot;false&quot;&gt;true
&lt;AutoReconnectBehavior UserControllable=&quot;false&quot;&gt;DisconnectOnSuspend&lt;/AutoReconnectBehavior&gt;
&lt;/AutoReconnect&gt;
&lt;AutoUpdate UserControllable=&quot;false&quot;&gt;true&lt;/AutoUpdate&gt;
&lt;RSASecurIDIntegration UserControllable=&quot;false&quot;&gt;Automatic&lt;/RSASecurIDIntegration&gt;
&lt;WindowsLogonEnforcement&gt;SingleLocalLogon&lt;/WindowsLogonEnforcement&gt;
&lt;WindowsVPNEstablishment&gt;LocalUsersOnly&lt;/WindowsVPNEstablishment&gt;
&lt;AutomaticVPNPolicy&gt;false&lt;/AutomaticVPNPolicy&gt;
&lt;PPPExclusion UserControllable=&quot;false&quot;&gt;Disable
&lt;PPPExclusionServerIP UserControllable=&quot;false&quot;&gt;&lt;/PPPExclusionServerIP&gt;
&lt;/PPPExclusion&gt;
&lt;EnableScripting UserControllable=&quot;false&quot;&gt;false&lt;/EnableScripting&gt;
&lt;EnableAutomaticServerSelection UserControllable=&quot;true&quot;&gt;false
&lt;AutoServerSelectionImprovement&gt;20&lt;/AutoServerSelectionImprovement&gt;
&lt;AutoServerSelectionSuspendTime&gt;4&lt;/AutoServerSelectionSuspendTime&gt;
&lt;/EnableAutomaticServerSelection&gt;
&lt;RetainVpnOnLogoff&gt;false&lt;/RetainVpnOnLogoff&gt;
&lt;AllowManualHostInput&gt;true&lt;/AllowManualHostInput&gt;
&lt;/ClientInitialization&gt;
&lt;ServerList&gt;
&lt;HostEntry&gt;
&lt;HostName&gt;example.com/&lt;/HostName&gt;
&lt;HostAddress&gt;example.com&lt;/HostAddress&gt;
&lt;UserGroup&gt;example&lt;/UserGroup&gt;
&lt;/HostEntry&gt;
&lt;/ServerList&gt;
&lt;/AnyConnectProfile&gt;</string>
<key>DISABLE_CUSTOMER_EXPERIENCE_FEEDBACK</key>
<string>false</string>
<key>DISABLE_VPN</key>
<string>false</string>
<key>DOWNLOAD_URL</key>
<string>https://company.com/cisco-secure-client-macos-5.1.1.42-predeploy-k9.dmg</string&gt;
<key>HIDE_UNINSTALLERS</key>
<string>1</string>
<key>NAME</key>
<string>Cisco Secure Client</string>
<key>PKGID</key>
<string>com.company.cisco.SecureClient</string>
<key>SOFTWARETITLE1</key>
<string>Secure</string>
<key>SOFTWARETITLE2</key>
<string>Client</string>
<key>SOFTWARETITLE3</key>
<string>All_Modules_Enabled</string>
<key>UMBRELLA_FINGERPRINT</key>
<string>2a7145a02f0b4b9799695b224af3f6c3</string>
<key>UMBRELLA_ORGANIZATION_ID</key>
<string>7775762</string>
<key>UMBRELLA_USER_ID</key>
<string>65034259</string>
<key>VENDOR</key>
<string>Cisco</string>
<key>VPN_XML</key>
<string>example.xml</string>
</dict>
<key>ParentRecipe</key>
<string>com.company.pkg.CiscoSecureClient-AllModulesEnabled</string>
<key>ParentRecipeTrustInfo</key>
<dict>
<key>non_core_processors</key>
<dict/>
<key>parent_recipes</key>
<dict>
<key>com.company.download.CiscoSecureClient</key>
<dict>
<key>path</key>
<string>~/Library/AutoPkg/RecipeRepos/com.company.autopkg_recipes/CiscoSecureClient.download.recipe</string>
<key>sha256_hash</key>
<string>711dc16d406d7a0197e507cbb227e058e974276743bd9108b0b8146525e256f6</string>
</dict>
<key>com.company.pkg.CiscoSecureClient-AllModulesEnabled</key>
<dict>
<key>path</key>
<string>~/Library/AutoPkg/RecipeRepos/com.company.autopkg_recipes/CiscoSecureClient-AllModulesEnabled.pkg.recipe</string>
<key>sha256_hash</key>
<string>845152bc12c86d485f5712c4361339250bc93f621ed3d02da4b88c3807d4c99f</string>
</dict>
</dict>
</dict>
</dict>
</plist>

]]>
Der Flounder : Jamf Pro 11.2.0 computer and mobile device enrollment permission changes https://derflounder.wordpress.com/2024/01/23/jamf-pro-11-2-0-computer-and-mobile-device-enrollment-permission-changes/ 2024-01-23T16:29:39+00:00 rtrouton As part of Jamf Pro 11.2.0 and later, Jamf has made a permissions change which affects enrollment. It is is mentioned in the 11.2.0 release notes in the Other Changes and Improvements section:

The Jamf Pro Server Action privilege, “Enroll Computers and Mobile Devices”, was split into two separate privileges.

What this means is that in Jamf Pro versions before 11.2.0, you had one permission:

  • Enroll Computers and Mobile Devices

As of Jamf Pro 11.2.0, there are now two permissions:

  • Enroll Computers
  • Enroll Mobile Devices

The important thing to know is that as part of upgrading to Jamf Pro 11.2.0, the Enroll Computers and Mobile Devices permission is removed, but the following permissions are not selected automatically:

  • Enroll Computers
  • Enroll Mobile Devices

To address this issue, if needed go into the Jamf Pro admin console following the upgrade to Jamf Pro 11.2.0 and select the new separate Enroll Computers and Enroll Mobile Devices permissions.

]]>
Der Flounder : Clearing the logged-in Microsoft SSO user on macOS using Microsoft’s Company Portal app https://derflounder.wordpress.com/2024/01/11/clearing-the-logged-in-microsoft-sso-user-on-macos-using-microsofts-company-portal-app/ 2024-01-11T15:33:10+00:00 rtrouton As part of working on a task recently, I ran into an issue with Microsoft’s Enterprise SSO plug-in on macOS. This plug-in enables single sign-on for Entra ID accounts for applications which support it. In this case, the issue was the following:

Desired behavior:

  1. Open application.
  2. Click the login button.
  3. Be prompted for the Entra ID user account to sign in with.
  4. Log in with that user account.

Actual behavior:

  1. Open application.
  2. Click the login button.
  3. Be automatically logged in as the Entra ID user registered for single sign-on.

Unfortunately for my use case, I really needed to have the application in question prompt the user for which account they needed to log in with because a user account other than the one registered for single sign-on needed to be able to sign in to the application in question.

After some discussion in the #jamf-intune-integration channel in the Mac Admins Slack, I was pointed towards a way to sign out the account which was enabled for single sign-on using Microsoft’s Company Portal application. With no account enabled for single sign-on, the application would now prompt for a user account to sign in with. For more details, please see below the jump.

To sign out the Entra ID account enabled for single sign-on using the Company Portal application, please use the procedure described below:

1. Open the Company Portal application.

2. Sign into the Company Portal application as the user of the computer.

3. Under the Company Portal menu in the menubar, select Settings…

4. In the Settings window, in the Single sign-on (SSO) section, click the Remove account from this device button.

Note: I’ve noticed that clicking the Remove account from this device button doesn’t make a noticeable change in the Settings window; the account still appears as enabled. However, clicking the button should do what’s needed and applications should now prompt for a user account.

]]>
Der Flounder : Blocking Jamf Pro’s macOS Onboarding feature using a macOS configuration profile https://derflounder.wordpress.com/2023/12/19/blocking-jamf-pros-macos-onboarding-feature-using-a-macos-configuration-profile/ 2023-12-19T22:34:05+00:00 rtrouton One of the new features of Jamf Pro 11.1.x and later is macOS Onboarding, which is a Self Service-based feature which provides a way to run a setup policy or policies. Rob Potvin has a good write-up on it here, which I recommend checking out:

https://www.motionbug.com/jamf-pro-and-macos-onboarding/

One of the things to be aware of with the new macOS Onboarding feature is that once the feature has been enabled, macOS Onboarding will run its associated policies on all Macs which don’t have the following user-level preference set:

  • Domain: com.jamfsoftware.selfservice.mac
  • Key: com.jamfsoftware.selfservice.onboardingcomplete
  • Value: Boolean (TRUE or FALSE)

 

Value set to FALSE (allowing macOS Onboarding to run):


<?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>com.jamfsoftware.selfservice.onboardingcomplete</key>
<false/>
</dict>
</plist>

 

Value set to TRUE (blocking macOS Onboarding from running):


<?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>com.jamfsoftware.selfservice.onboardingcomplete</key>
<true/>
</dict>
</plist>

 

Fortunately, it’s possible to add this setting with a value of TRUE to a macOS configuration profile and deploy the profile to all Macs that you don’t want to run macOS Onboarding on.

For those who would need this, I have an example macOS configuration profile with com.jamfsoftware.selfservice.onboardingcomplete set to TRUE available via the link below:

https://github.com/rtrouton/profiles/tree/main/JamfProSelfServiceOnboardingCompleted

]]>