SCRIPT FOR APPLICATION USAGE in the device in SMART GROUP

Hasibravo
New Contributor III

I found this script very useful and extracting the list of application usage in the Smart Group. 

 

#!/bin/bash

# This script uses the api to gather all serial numbers of a computer group and then gathers application usage data for that mac.
# each entry contains User data so, multiple lines will exist for each serial. one for each application.
# exported report is stored in a coma sperated spreadsheet.
# Please see end of script for terms
###
# Environment Specific Variables can be left blank if passing parameters from jamf
###
# hardcode here for testing

api_user=""
api_pass=""
jamf_url=""
# A Jamf Pro computer group (static or smart) that contains the client for which you want a report
group_name=''
# Number of days in report (using today as the end date...)
days=

# Check for passed parameters from jamf
if [ "$4" != "" ]; then
	api_user=$4
fi

if [ "$5" != "" ]; then
	api_pass=$5
fi

if [ "$6" != "" ]; then
	jamf_url=$6
fi

if [ "$7" != "" ]; then
	group_name=$7
fi

if [ "$8" != "" ]; then
	days=$8
fi

# Location of jamfHelper
jamfHelper="/Library/Application Support/JAMF/bin/jamfHelper.app/Contents/MacOS/jamfHelper"

# For testing only if you have a test JSS with a self signed SSL cert...
# Set security='' for normal use. Set security='--insecure' for testing
security=''
#security='--insecure'

# URL encode function
urlencode() {
	old_lc_collate=$LC_COLLATE
	LC_COLLATE=C
	local length="${#1}"
	for (( i = 0; i < length; i++ )); do
		local c="${1:i:1}"
		case $c in
			[a-zA-Z0-9.~_-]) printf "$c" ;;
			*) printf '%%%02X' "'$c" ;;
		esac
	done
	LC_COLLATE=$old_lc_collate
}

# URL encode the group name
group_name=$( urlencode "$group_name" )

# Pretty file name
filename="Application Usage Report - $days day "

# Today's date minus the days
start_date=$(date -j -v-${days}d +"%Y"-"%m"-"%d")

# Today's date
end_date=$(date +"%Y-%m-%d")

# Timestamp for filename
timestamp=$(date +"%Y-%m-%d-%H%M%S")

# Getting the Bearer token
jamfTokenCurl=$(curl -s -u "$api_user:$api_pass" "$jamf_url/api/v1/auth/token" -X POST)
jamfBearerToken=$(echo "$jamfTokenCurl" | jq -r .token)




# If you want, this dialog box will let the user know what's about to happen
buttonClicked=$("$jamfHelper" \
		-windowType utility \
		-title "Application Usage Report" \
		-heading "Please click OK to build the report." \
		-description "This process may take a few minutes depending on how many computers are in the report. We'll let you know when we're finished." \
		-button1 "Okay" \
		-defaultButton 1 \
		-icon "/System/Library/CoreServices/CoreTypes.bundle/Contents/Resources/AlertNoteIcon.icns")

# Build the API path to the smartgroup
api_path="JSSResource/computergroups/name/${group_name}"

# Get the currently logged in user for the output file path
loggedInUser=$(scutil <<< "show State:/Users/ConsoleUser" | awk '/Name :/ { print $3 }')
loggedInUserHome=$(dscl . read /Users/$loggedInUser NFSHomeDirectory | cut -d' ' -f2-)

# Getting Serial Numbers
echo "Getting a list of devices in Jamf Pro group. "
URL="${jamf_url}/${api_path}"
responseXML=$(curl ${security} --silent --show-error --write-out "\n%{http_code}" \
							--header "Authorization: Bearer ${jamfBearerToken}" \
							--header "Accept: text/xml" "${URL}")
HTTP_Status=$(echo "$responseXML" | tail -1)
responseXML=$(echo "$responseXML" | sed \$d)

echo "HTTP_Status : $HTTP_Status"
/bin/echo -n "HTTP Status Code: $HTTP_Status : "
if [[ $HTTP_Status = "200" ]]; then
	echo '[OK]'
elif [[ $HTTP_Status = "400" ]]; then
	echo "[error] Invalid API request"
	exit 1
else
	echo "[error] API could not return the group information. "
	echo "$responseXML"
	exit 1
fi

serialnumbers=($(echo "$responseXML" | xsltproc /tmp/stylesheet.xslt -))

echo "Serial number list : ${serialnumbers[@]}"
device_count=${#serialnumbers[@]}
touch /tmp/export.csv

# Write the header values of each column
echo "Full Name,Username,Position,Department,Building,Room,Email Address,Model,Serial Number,App Name,Version Number,Minutes in Forground,Minutes Open" > /tmp/export.csv

for serial in "${serialnumbers[@]}"; do
	echo "Requesting details for device serial number : ${serial}"
	api_device_path="JSSResource/computers/serialnumber/$serial"
	location_data=$(curl ${security} --silent --show-error \
									--header "Authorization: Bearer ${jamfBearerToken}" \
									--header "Accept: text/xml" "$jamf_url/$api_device_path")
	username=$(echo $location_data | /usr/bin/awk -F'<username>|</username>' '{print $2}')
	realname=$(echo $location_data | /usr/bin/awk -F'<realname>|</realname>' '{print $2}')
	email_address=$(echo $location_data | /usr/bin/awk -F'<email_address>|</email_address>' '{print $2}')
	position=$(echo $location_data | /usr/bin/awk -F'<position>|</position>' '{print $2}')
	department=$(echo $location_data | /usr/bin/awk -F'<department>|</department>' '{print $2}')
	room=$(echo $location_data | /usr/bin/awk -F'<room>|</room>' '{print $2}')
	building=$(echo $location_data | /usr/bin/awk -F'<building>|</building>' '{print $2}')
	model=$(echo $location_data | /usr/bin/awk -F'<model>|</model>' '{print $2}')

	echo "\$username      : \"${username}\""
	echo "\$realname      : \"${realname}\""
	echo "\$email_address : \"${email_address}\""
	echo "\$position      : \"${position}\""
	echo "\$department    : \"${department}\""
	echo "\$room          : \"${room}\""
	echo "\$building      : \"${building}\""
	echo "\$model         : \"${model}\""

	api_path="JSSResource/computerapplicationusage/serialnumber/$serial/${start_date}_${end_date}"
	xmlResponse=$(curl ${security} --silent --show-error \
								--header "Authorization: Bearer ${jamfBearerToken}" \
								--header "Accept: text/xml" "$jamf_url/$api_path")

	cat << EOF > /tmp/stylesheet.xslt
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
		<xsl:output method="text"/>
		<xsl:template match="/">
				<xsl:for-each select="computer_application_usage/usage/apps/app">
						<xsl:text>${realname//,}</xsl:text>
						<xsl:text>,</xsl:text>
						<xsl:text>${username//,}</xsl:text>
						<xsl:text>,</xsl:text>
						<xsl:text>${position//,}</xsl:text>
						<xsl:text>,</xsl:text>
						<xsl:text>${department//,}</xsl:text>
						<xsl:text>,</xsl:text>
						<xsl:text>${room//,}</xsl:text>
						<xsl:text>,</xsl:text>
						<xsl:text>${building//,}</xsl:text>
						<xsl:text>,</xsl:text>
						<xsl:text>${email_address//,}</xsl:text>
						<xsl:text>,</xsl:text>
						<xsl:text>${model//,}</xsl:text>
						<xsl:text>,</xsl:text>
						<xsl:text>$serial</xsl:text>
						<xsl:text>,</xsl:text>
						<xsl:value-of select="name"/>
						<xsl:text>,</xsl:text>
						<xsl:value-of select="version"/>
						<xsl:text>,</xsl:text>
						<xsl:value-of select="foreground"/>
						<xsl:text>,</xsl:text>
						<xsl:value-of select="open"/>
						<xsl:text>
						</xsl:text>
				</xsl:for-each>
		</xsl:template>
</xsl:stylesheet>
EOF

	usageData=$(echo "$xmlResponse" | xsltproc /tmp/stylesheet.xslt -)
	if [[ -z "$usageData" ]]; then
		echo "No application usage data found for serial number ${serial}."
	else
		echo "$usageData" >> /tmp/export.csv
	fi
done

# Output file path
outputFilePath="${loggedInUserHome}/Desktop/${filename} - ${timestamp}.csv"

# Move the file to the user's desktop
mv /tmp/export.csv "$outputFilePath"

# Notify the user that the report is complete
"$jamfHelper" \
	-windowType utility \
	-title "Application Usage Report" \
	-heading "Report Complete" \
	-description "The application usage report has been saved to your Desktop." \
	-button1 "OK" \
	-defaultButton 1 \
	-icon "/System/Library/CoreServices/CoreTypes.bundle/Contents/Resources/AlertNoteIcon.icns"

# Invalidate the Bearer token
curl -s -H "Authorization: Bearer ${jamfBearerToken}" -X POST "${jamf_url}/api/v1/auth/invalidate-token"

echo "Done! Check ${outputFilePath} for the report."
2 REPLIES 2

jhorowitz22
New Contributor

Thanks for putting thr work in here. 

I'm attempting to use this and I'm getting a "Getting a list of devices in Jamf Pro group.
HTTP_Status : 401
HTTP Status Code: 401 : [error] API could not return the group information." error

Any suggestions? 

it looks like it is using the classic API, so be sure to use the correct credentials when authenticating for the calls...