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.
0 Comments
No Comments