Deploying Nexthink on macOS devices using Jamf can streamline your monitoring and analytics processes, ensuring that all devices are equipped with the Nexthink Collector. This blog post walks you through the steps involved in deploying Nexthink through Jamf, ensuring a smooth and effective installation. Here’s a detailed guide to help you achieve that.
Nexthink deployment through Jamf includes following steps:
- Prepare the Nexthink Script
- Create and configure Nexthink Policy in Jamf ConsoleÂ
- Create and configure Jamf profile in Jamf ConsoleÂ
Step 1: Prepare the Nexthink Script:
The Nexthink script plays a crucial role in automating the deployment of the Nexthink Collector on macOS devices. This script is available in the Nexthink documentation and should be updated accordingly whenever Nexthink releases a new version. One critical part of the script is the hash string that verifies the authenticity of the installation package.
- Important: The hash string in the script should be checked and updated regularly as Nexthink releases updates. For the latest version, refer to Nexthink’s SHA256 Hash String.
- Note: Ensure that the options marked in bold within the script are updated before deployment.
### START OF SAMPLE SCRIPT ###
#!/bin/zsh
### PARAMETERS OF THE INSTALL ###
ALLOW_UPGRADE=”new”
# Possible values of ALLOW_UPGRADE, if an existing version is installed:
# “new”: Only upgrade if there is a new version available
# “only-updater”: Only upgrade old versions without auto-update
# “always”: Always overwrite an existing version
# “never”: Do not upgrade an existing version
#Â CLEAN_INSTALL=”false” :Discard any previously existing configuration
# See https://docs.nexthink.com/platform/latest/installing-collector-on-macos
Readonly COLLECTOR_URL=”https://download.nexthink.com/releases/latest/OSX_Collector/Nexthink_Collector.dmg”
#readonly COLLECTOR_SHA256=”b36d117a24a8b4f929a0d3542d1e697b5c7bdb9c8fe47debd9e4038f2de01b44″Â
# Get your hash string, for example from https://download.nexthink.com/releases/latest/OSX_Collector/Nexthink_Collector.dmg.sha256
readonly COLLECTOR_SHA256=”ad40646d67b937bf8deb24d93246fdb5b55dd2d2f3d7a6d0bde36493f11da96b”
readonly ADDRESS=”NEXTHINK INSTANCE ADDRESS” # Nexthink instance address, example: “acme-01.eu.nexthink.com”
readonly TCP_PORT=”443″ # Nexthink instance port, example: 443
readonly KEY=”—–BEGIN CUSTOMER KEY———-END CUSTOMER KEY—–” # Enter Your customer key
# Optional parameters:
readonly ROOT_CA=”” # Only for old releases, leave empty if not needed
# Other install parameters to customize:
readonly OTHER_CSI_PARAMS=”–engage enable \
                       –use_assignment enable \
                       –ra_execution_policy signed_trusted_or_nexthink \
                       –anonymize_username false \
                       –windows_focus_time_monitoring true \
                       –user_interaction_time_monitoring enable \
                       –anonymize_wifi_network false”
#########################################################################################################
### Do not change from here ###
readonly MOUNT_POINT=”/Volumes/Nexthink_Collector”
readonly DATA_DIR=”/Library/Logs/Microsoft/IntuneScripts/NexthinkCollector” # The location of logs and temporary data
readonly DMG_FILE=”$DATA_DIR/Nexthink_Collector.dmg”
readonly KEY_FILE=”$DATA_DIR/customer_key.txt”
readonly ROOTCA_FILE=”$DATA_DIR/rootca.txt”
readonly APP_NAME=”NexthinkCollector”
readonly LOG_FILE=”$DATA_DIR/$APP_NAME.log”
readonly META_FILE=”$DATA_DIR/$APP_NAME.meta”
readonly APP_DIR=”/Library/Application Support/Nexthink”
function log() {
                       echo “$(date) | $*”
}
function fail() {
                       log “$*”
                       cleanup
                       exit 1
}
function createMetaDirectory() {
                       if [[ ! -d “$DATA_DIR” ]]; then
                      ## Creating Metadirectory
                      log “Creating [$DATA_DIR] to store logs and temporary data”
                       mkdir -p “$DATA_DIR”
                       fi
}
function checkDmgHash() {
                       local dmgHash=$(/usr/bin/shasum -a 256 “$DMG_FILE” | /usr/bin/grep -oE ‘^\w+’)
                       if [[ “$COLLECTOR_SHA256” != “$dmgHash” ]]; then
                      return 1
                       fi
                       return 0
}
function isProcessAlive() {
                       for i in $(seq 100); do
                      if pgrep “$1” > /dev/null 2>&1; then
                     return 0
                      fi
                      sleep 0.1
                       done
                       return 1
}
# Function to update the last modified date for this app
fetchLastModifiedDate() {
                       # get the last modified date of the file we need to download
                       COLLECTOR_URL_MODIFIED_DATE=$(curl -sIL “$COLLECTOR_URL” | grep -i “last-modified” | awk ‘{$1=””; print $0}’ | awk ‘{ sub(/^[ \t]+/, “”); print }’ | tr -d ‘\r’)
}
updateMetaLastModified() {
                       log “Writing last modified date [$1] to [$META_FILE]”
                       echo “$1” > “$META_FILE”
}
# Function to check if we need to update or not
function updateCheck() {
                       log “Checking if we need to install or update [$APP_NAME] with ALLOW_UPGRADE = [$ALLOW_UPGRADE]”
                       ## Is the app already installed?
                       if [ -d “$APP_DIR” ]; then
                      # Get the version of the collector to validate its able to auto-update
                      local nxtversion=$(/usr/bin/grep version “$APP_DIR/config.json” | /usr/bin/cut -d'”‘ -f4)
                      log “Found [$APP_NAME] version [$nxtversion] already installed”
                      if [[ $ALLOW_UPGRADE == “never” ]]; then
                     fail “Upgrading is not allowed”
                      fi
                      if [[ $ALLOW_UPGRADE == “always” ]]; then
                     log “ALLOW_UPGRADE = [$ALLOW_UPGRADE], so proceeding with install”
                     return
                      fi
                      OLDIFS=$IFS
                      IFS=’.’ read nxvers_major nxvers_minor nxvers_dot1 nxvers_dot2<<< “$(echo $nxtversion)”
                      IFS=$OLDIFS
                      # App is installed, if it’s updates are handled by MAU we should quietly exit
                      local isOldVersion=”false”
                      if [[ ${nxvers_major} -le 22 ]]; then
                     if [[ ${nxvers_minor} -le 8 ]]; then
                      isOldVersion=”true”
                     fi
                      fi
                      if [[ $isOldVersion != “true” ]] && [[ $ALLOW_UPGRADE == “only-updater” ]]; then
                     log “[$APP_NAME] version [$nxtversion] is already installed and handles updates itself”
                     log “Exiting, nothing to do”
                     exit 0
                      fi
                      # App is already installed, we need to determine if it requires updating or not
                       fetchLastModifiedDate
                      ## Did we store the last modified date last time we installed/updated?
                      if [ -f “$META_FILE” ]; then
                      previousLastModifiedDate=$(cat “$META_FILE”)
                     if [[ “$previousLastModifiedDate” != “$COLLECTOR_URL_MODIFIED_DATE” ]]; then
                     log “Update found, previous [$previousLastModifiedDate] differs from current [$COLLECTOR_URL_MODIFIED_DATE]”
                     else
                     log “No changes between previous update [$previousLastModifiedDate] and current [$COLLECTOR_URL_MODIFIED_DATE]”
                     log “Exiting, nothing to do”
                     exit 0
                     fi
                      else
                     log “Meta file [$META_FILE] not found”
                     log “Unable to determine if update required, updating [$APP_NAME] anyway”
                      fi
                       else
                      echo “$(date) | No previous versions of [$APP_NAME] found”
                       fi
}
function downloadDmg() {
                       log “Downloading Collector from [$COLLECTOR_URL] to [$DMG_FILE]”
                       curl -f -s –connect-timeout 30 –retry 5 –retry-delay 60 \
                      –compressed -L -o “$DMG_FILE” “$COLLECTOR_URL” \
                      || fail “Failure to download [$COLLECTOR_URL] to [$DMG_FILE]”
                        checkDmgHash || fail “Invalid hash, [$DMG_FILE] is corrupted”
                       log “File downloaded and verified”
}
function installCollector() {
                       log “Installing [$APP_NAME]”
                       log “Setting up environment”
                       echo -n “$KEY” > “$KEY_FILE”
                       if [ ! -z “$ROOT_CA” ]; then
                      echo “$ROOT_CA” > “$ROOTCA_FILE”
                       fi
Â
                       log “Mounting Image at [$MOUNT_POINT]”
                       # Mount the DMG
                        hdiutil attach -mountpoint “$MOUNT_POINT” -quiet -nobrowse -noautoopen “$DMG_FILE”
                       # Define the parameters for csi.app for installing the Collector from the command line interface
                       log “Executing [$APP_NAME] csi install process”
                       local CSI_PARAMS=”$OTHER_CSI_PARAMS -log_mode trace”
                       if [[ “$CLEAN_INSTALL” == “true” ]]; then
                      CSI_PARAMS=”$CSI_PARAMS –clean_install”
                       fi
                       if [ -z “$ROOT_CA” ]; then
                      “$MOUNT_POINT”/csi.app/Contents/MacOS/csi \
                     -address “$ADDRESS” -tcp_port “$TCP_PORT” \
                     -key “$KEY_FILE” \
                     ${=CSI_PARAMS} \
                       exitcode=$?
                       else
                      “$MOUNT_POINT”/csi.app/Contents/MacOS/csi \
                     -address “$ADDRESS” -tcp_port “$TCP_PORT” \
                     -rootca “$ROOTCA_FILE” -key “$KEY_FILE” \
                     ${=CSI_PARAMS}
                       exitcode=$?
                       fi
                       if [[ $exitcode != 0 ]]; then
                      fail “Failed to install [$APP_NAME]”
                       fi
                       log “[$APP_NAME] Installed”
                        fetchLastModifiedDate
                        updateMetaLastModified $COLLECTOR_URL_MODIFIED_DATE
                       log “Restart Coordinator Services”
                        launchctl unload /Library/LaunchDaemons/com.nexthink.collector.nxtcoordinator.plist
                        launchctl load /Library/LaunchDaemons/com.nexthink.collector.nxtcoordinator.plist
                       # Check if Collector services are running
                       for p in nxtcoordinator nxtsvc; do
                      log “Checking if [$p] is alive”
                       isProcessAlive “$p” || fail “Service [$p] cannot be started”
                       done
}
function cleanup() {
                       # Unmount the DMG
                       log “Un-mounting “
                        hdiutil detach “$MOUNT_POINT” -force
                       # Delete the package contents
                       log “Removing temporary files”
                       rm -f “$DMG_FILE” “$KEY_FILE” “$ROOTCA_FILE”
}
function startLog() {
                        createMetaDirectory
                       exec > >(tee -a “$LOG_FILE”) 2>&1
}
startLog
echo “”
echo “###################################################”
log “Logging install of [$APP_NAME] to [$LOG_FILE]”
echo “##################################################”
echo “”
updateCheck
downloadDmg
installCollector
cleanup
exit 0
### END OF SAMPLE Script ###
Step 2: Create and Configure Nexthink Policy in Jamf Console
The next step is to create and configure the Nexthink policy in the Jamf console. This policy defines how the Nexthink Collector will be deployed across devices.
-
Create and Name the Policy:
-
Name the policy according to your company’s naming convention to maintain consistency and organization.Â
-
-
Add Script to the Policy:
-
Attach the Nexthink script to the policy to enable deployment.
-
-
Save the Policy:
-
After configuring the script, save the policy to finalize the setup.
-
Step 3: Creating the Nexthink Profile in Jamf Console
A configuration profile ensures that the Nexthink Collector is set up with the correct settings on all macOS devices. This profile needs to be created within the Jamf console and configured according to Nexthink’s installation guidelines.
-
Create the Nexthink Configuration Profile:
-
In Jamf Console, navigate to Configuration Profiles and start creating a new configuration profile for Nexthink.
-
-
Upload Configuration:
-
Preference Domains:
-
com.nexthink.nxtcod
-
Property list:
<?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">
<plist version=”1″><dict><key>PayloadUUID</key><string>2E939D84-BFDC-4C5C-8381-A02F5B5CA61E</string><key>PayloadType</key><string>Configuration</string><key>PayloadOrganization</key><string>Nexthink Dev</string><key>PayloadIdentifier</key><string>2E939D84-BFDC-4C5C-8381-A02F5B5CA61E</string><key>PayloadDisplayName</key><string>Nexthink Configuration</string><key>PayloadDescription</key><string>Configuration profile to grant access to Nexthink Collector to the file system</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>PayloadUUID</key><string>8F7B4C92-CAB9-43DD-B37C-A717112F1B3D</string><key>PayloadType</key><string>com.apple.TCC.configuration-profile-policy</string><key>PayloadOrganization</key><string>Nexthink Dev</string><key>PayloadIdentifier</key><string>8F7B4C92-CAB9-43DD-B37C-A717112F1B3D</string><key>PayloadDisplayName</key><string>Privacy Preferences Policy Control</string><key>PayloadDescription</key><string/><key>PayloadVersion</key><integer>1</integer><key>PayloadEnabled</key><true/><key>Services</key><dict><key>SystemPolicyAllFiles</key><array><dict><key>Identifier</key><string>com.nexthink.nxtcod</string><key>CodeRequirement</key><string>identifier “com.nexthink.nxtcod” and anchor apple generic and certificate 1[field.1.2.840.113635.100.6.2.6] /* exists */ and certificate leaf[field.1.2.840.113635.100.6.1.13] /* exists */ and certificate leaf[subject.OU] = PDEKAZ43QL</string><key>IdentifierType</key><string>bundleID</string><key>StaticCode</key><integer>0</integer><key>Allowed</key><integer>1</integer></dict></array></dict></dict></array></dict></plist>%Â
-
-
com.nexthink.nxtsvcapp
-
<?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“>Â
<plist version=”1.0″>Â
<dict>Â
               <key>PayloadContent</key>Â
               <array>Â
                              <dict>Â
                                               <key>PayloadDescription</key>Â
                                               <string/>Â
                                               <key>PayloadDisplayName</key>Â
                                               <string>Privacy Preferences Policy Control</string>Â
                                               <key>PayloadIdentifier</key>Â
                                               <string>10CE4089-0447-411C-8B8B-C3D39AE2014C</string>Â
                                               <key>PayloadOrganization</key>Â
                                               <string>Nexthink</string>Â
                                               <key>PayloadType</key>Â
                                               <string>com.apple.TCC.configuration-profile-policy</string>Â
                                               <key>PayloadUUID</key>Â
                                               <string>FD92EF16-0B18-4375-984A-B9F23EAB3315</string>Â
                                               <key>PayloadVersion</key>Â
                                               <integer>1</integer>Â
                                               <key>Services</key>Â
                                               <dict>Â
                                                              <key>SystemPolicyAllFiles</key>Â
                                                               <array>Â
                                                                               <dict>Â
                                                                                            <key>Allowed</key>Â
                                                                                               <true/>Â
                                                                                               <key>CodeRequirement</key>Â
                                                                                               <string>identifier “com.nexthink.nxtsvcapp” and anchor apple generic and certificate 1[field.1.2.840.113635.100.6.2.6] /* exists */ and certificate leaf[field.1.2.840.113635.100.6.1.13] /* exists */ and certificate leaf[subject.OU] = PDEKAZ43QL</string>Â
                                                                                               <key>Comment</key>Â
<string>
</string>Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â <key>Identifier</key>Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â <string>com.nexthink.nxtsvcapp</string>Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â <key>IdentifierType</key>Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â <string>bundleID</string>Â
                                                                               </dict>Â
                                                               </array>Â
                                               </dict>Â
                               </dict>
               </array>Â
               <key>PayloadDescription</key>Â
               <string/>Â
               <key>PayloadDisplayName</key>Â
               <string>Nexthink – nxtsvc – Full Disk Access</string>Â
        <key>PayloadIdentifier</key>Â
               <string>10CE4089-0447-411C-8B8B-C3D39AE2014C</string>Â
               <key>PayloadOrganization</key>Â
               <string>Nexthink</string>Â
               <key>PayloadScope</key>Â
               <string>System</string>Â
               <key>PayloadType</key>Â
               <string>Configuration</string>Â
               <key>PayloadUUID</key>Â
               <string>3BADD243-3D0A-4194-A444-D9C0D203327C</string>Â
               <key>PayloadVersion</key>Â
               <integer>1</integer>Â
               <key>PayloadRemovalDisallowed</key>Â
               <true/>Â
</dict>Â
</plist>
-
Privacy Preferences Policy Control:
-
This section ensures that the Nexthink Collector has the required access permissions.
-
-
App Access Settings:
-
Identifier:Â com.nexthink.nxtsvcapp
-
Identifier Type:Â Bundle Id
-
Code Requirement:
- identifier “com.nexthink.nxtsvcapp” and anchor apple generic and certificate 1[field.1.2.840.113635.100.6.2.6] /* exists */ and certificate leaf[field.1.2.840.113635.100.6.1.13] /* exists */ and certificate leaf[subject.OU] = PDEKAZ43QL
-
-
App Access Settings:
-
Identifier:Â com.nexthink.nxtcod
-
Identifier Type: Bundle Id
-
Code requirement:
- identifier “com.nexthink.nxtsvcapp” and anchor apple generic and certificate 1[field.1.2.840.113635.100.6.2.6] /* exists */ and certificate leaf[field.1.2.840.113635.100.6.1.13] /* exists */ and certificate leaf[subject.OU] = PDEKAZ43QL
-
-
Finalize the Configuration:
- After setting up the preferences and privacy controls, save the configuration profile.
Additional Resources:
For detailed instructions on creating the Nexthink profile, refer to the official Nexthink documentation:Â Installing Nexthink Collector on macOS.
Conclusion:
Deploying Nexthink through Jamf simplifies the process of managing your macOS devices. By following these steps, you can ensure that the Nexthink Collector is installed with the necessary configurations and privacy settings to monitor and analyze your devices effectively. Always remember to update the hash string in the script and adjust your policies and profiles as required by Nexthink’s updates.