Files
MacAdmin/Jamf Pro/Reporting/jamf_verifyVPPApps.sh
Zack T 2efa3c0e9c v1.0.0 = Initial Version
+ Initial Version

(Re-organizing after merging repos.)
2020-10-01 14:04:47 -07:00

233 lines
9.5 KiB
Bash

#!/bin/bash
###################################################################################################
# Script Name: jamf_verifyVPPApps.sh
# By: Zack Thompson / Created: 6/13/2018
# Version: 1.0 / Updated: 10/8/2018 / By: ZT
#
# Description: Gets details on all of the Mobile Device VPP Apps from the JSS and checks the iTunes API to see if they are available, update to date, and 32bit only, and exports results to a csv file.
#
# Inspired by:
# https://github.com/dnikles/Check32bitOnlyApps/blob/master/check32bit.sh
# https://github.com/bumbletech/JSS_API_scripts/blob/master/JSS_API_check_stale_and_32bit_apps.sh
# https://www.jamf.com/jamf-nation/discussions/28035/identifying-32-bit-apps-for-ios-before-vpp-purchase
#
###################################################################################################
echo "***** verifyVPPApps process: START *****"
##################################################
# Define Variables
# Either hard code or prompt for credentials
# jamfAPIUser="APIUsername"
# jamfAPIPassword="APIPassword"
jamfAPIUser=$(/usr/bin/osascript -e 'set userInput to the text returned of (display dialog "Enter your Jamf Username:" default answer "")' 2>/dev/null)
jamfAPIPassword=$(/usr/bin/osascript -e 'set userInput to the text returned of (display dialog "Enter your Jamf Password:" default answer "" with hidden answer)' 2>/dev/null)
jamfPS="https://newjss.company.com:8443"
mobileApps="${jamfPS}/JSSResource/mobiledeviceapplications"
mobileAppsByID="${mobileApps}/id"
# Add -k (--insecure) to disable SSL verification
curlJamfAPI=(--silent --show-error --fail --user "${jamfAPIUser}:${jamfAPIPassword}" --write-out "statusCode:%{http_code}" --output - --header "Accept: application/xml" --request)
iTunesAPI="https://uclient-api.itunes.apple.com/WebObjects/MZStorePlatform.woa/wa/lookup?version=1&p=mdm-lockup&caller=MDM&platform=itunes&cc=us&l=en&id="
curliTunesAPI=(--silent --show-error --fail --write-out "statusCode:%{http_code}" --output - --header "Accept: application/JSON" --request)
# Either use CLI arguments or prompt for choice
if [[ "${4}" == "Jamf" ]]; then
action=$(/usr/bin/osascript -e 'tell application (path to frontmost application as text)' -e 'set availableActions to {"Get VPP Apps", "Assign VPP Apps"}' -e 'set Action to choose from list availableActions with prompt "Select action:" default items {"Get VPP Apps"}' -e 'end tell' 2>/dev/null)
ranBy="Jamf"
else
action="${1}"
switch1="${2}"
ranBy="CLI"
fi
##################################################
# Setup Functions
getHelp() {
echo "
usage: jamf_verifyVPPApps.sh [-run | -r] [/path/to/file.csv] [-help]
Info: Gets details on all of the VPP Apps from the JSS and checks iTunes to see if they are 32bit and exports results to a csv file.
Actions:
--run | -r Run the report and output to a csv file.
Example: jamf_verifyVPPApps.sh -run output.csv
--help Displays this help section.
Example: jamf_verifyVPPApps.sh -help
"
}
getAppInfo() {
echo "Requesting list of all App IDs..."
# GET list of App IDs from the JSS.
curlReturn="$(/usr/bin/curl "${curlJamfAPI[@]}" GET $mobileApps)"
# Check if the API call was successful or not.
curlCode=$(echo "${curlReturn}" | /usr/bin/awk -F statusCode: '{print $2}' | /usr/bin/xargs )
checkStatusCode $curlCode $appID
numberOfApps=$(echo "${curlReturn}" | /usr/bin/sed -e 's/statusCode\:.*//g' | /usr/bin/xmllint --format - | /usr/bin/xpath /mobile_device_applications/size 2>/dev/null | LANG=C /usr/bin/sed -e 's/<[^/>]*>//g' | LANG=C /usr/bin/sed -e 's/<[^>]*>/\'$'\n/g')
echo "Total Number of Apps: $numberOfApps"
# Set the ulimit as a large number of apps will start causing steps to error out because of "cannot make pipe for command substitution: Too many open files"
ulimitSize=$((numberOfApps + 256))
ulimit -n $ulimitSize
# Regex down to just the ID numbers
appIDs=$(echo "${curlReturn}" | /usr/bin/sed -e 's/statusCode\:.*//g' | /usr/bin/xmllint --format - | /usr/bin/xpath /mobile_device_applications/mobile_device_application/id 2>/dev/null | LANG=C /usr/bin/sed -e 's/<[^/>]*>//g' | LANG=C /usr/bin/sed -e 's/<[^>]*>/\'$'\n/g')
informBy "Getting info for each App from the JSS..."
# For Each ID, get additional information.
for appID in $appIDs; do
# Get each info for each AppID
curlReturn="$(/usr/bin/curl "${curlJamfAPI[@]}" GET ${mobileAppsByID}/${appID}/subset/General)"
# Check if the API call was successful or not.
curlCode=$(echo "${curlReturn}" | /usr/bin/awk -F statusCode: '{print $2}' | /usr/bin/xargs )
checkStatusCode $curlCode $appID
# Regex down to the info we want.
appInfo="$(echo "${curlReturn}" | /usr/bin/sed -e 's/statusCode\:.*//g' | /usr/bin/xmllint --format - | /usr/bin/xpath '/mobile_device_application/general/id | /mobile_device_application/general/name | /mobile_device_application/general/site/name | /mobile_device_application/general/bundle_id | /mobile_device_application/general/version | /mobile_device_application/general/itunes_store_url' 2>/dev/null | LANG=C /usr/bin/sed -e 's/<[^/>]*>//g' | LANG=C /usr/bin/sed -e 's/<[^>]*>/\'$'\t/g' | LANG=C /usr/bin/sed -e 's/\'$'\t[^\t]*$//')"
# Read in the App Info and assign to variables
while IFS=$'\t' read appID appName bundle_id jssVersion itunes_store_url appSite; do
# Get the Adam ID which is needed for the iTunes API
appAdamID=$(echo "${itunes_store_url}" | /usr/bin/sed -e 's/.*\/id\(.*\)?.*/\1/')
# Get App info from iTunes
iTunesCurlReturn="$(/usr/bin/curl "${curliTunesAPI[@]}" GET ${iTunesAPI}${appAdamID})"
# Check if the API call was successful or not.
curlCode=$(echo "${iTunesCurlReturn}" | /usr/bin/awk -F statusCode: '{print $2}' | /usr/bin/xargs )
checkStatusCode $curlCode $appID "iTunes" $appAdamID
# echo "AppID: ${appID} AdamID: ${appAdamID}"
# Check if the App still exists on iTunes
appExists=$(echo "${iTunesCurlReturn}" | /usr/bin/sed -e 's/statusCode\:.*//g' | /usr/bin/python -c "
import sys, json
objects = json.load(sys.stdin)
if not objects.get('results'):
print('No')
else:
print('Yes')")
# Extract if the App is 32bit Only
bitness32=$(echo "${iTunesCurlReturn}" | /usr/bin/sed -e 's/statusCode\:.*//g' | /usr/bin/python -mjson.tool | /usr/bin/awk -F "is32bitOnly\": " '{print $2}' | /usr/bin/xargs | /usr/bin/sed 's/,//')
# Extract the Version from iTunes
iTunesVersion=$(echo "${iTunesCurlReturn}" | /usr/bin/sed -e 's/statusCode\:.*//g' | /usr/bin/python -mjson.tool | /usr/bin/awk -F "\"display\":" '{print $2}' | /usr/bin/xargs | /usr/bin/sed 's/,//')
# echo "iTunes: ${iTunesVersion} and JSS: ${jssVersion}"
# Check if the jssVersion is Out of Date
if [[ -n "${iTunesVersion}" && "${iTunesVersion}" == "${jssVersion}" ]]; then
outOfDate="No"
else
outOfDate="Yes"
fi
# Output to File
echo -e "${appInfo}\t${appExists}\t${outOfDate}\t${iTunesVersion}\t${bitness32}" >> "${outFile}"
# Clear variable for next loop item
unset appInfo
done < <(/usr/bin/printf '%s\n' "${appInfo}")
done
informBy "List has been saved to: ${outFile}"
}
checkStatusCode() {
if [[ $1 != "200" ]]; then
informBy "ERROR: API call failed for ${2} with error: ${1}!"
if [[ $3 == "iTunes" ]]; then
informBy "AdamID is: ${4}"
fi
fi
}
fileExists() {
if [[ ! -e "${1}" && $2 == "create" ]]; then
echo "Creating output file at location: ${1}"
/usr/bin/touch "${1}"
echo "Adding header to output file..."
header="App ID\tApp Name\tBundle ID\tJSS Version\tiTunes Store URL\tJamf Site\tAvailable\tOut Of Date\tiTunes Version\t32Bit"
echo -e $header >> "${outFile}"
elif [[ ! -e "${1}" && $2 == "trip" ]]; then
informBy "ERROR: Unable to find the input file!"
echo "***** verifyVPPApps process: FAILED *****"
exit 3
fi
}
informBy() {
case $ranBy in
Jamf )
/usr/bin/osascript -e 'tell application (path to frontmost application as text) to display dialog "'"${1}"'" buttons {"OK"}' > /dev/null
;;
CLI )
echo "${1}"
;;
esac
}
##################################################
# Bits Staged
# Verify credentials were provided.
if [[ -z "${jamfAPIUser}" && -z "${jamfAPIPassword}" ]]; then
informBy "Jamf credentials are required!"
exit 1
else
# Verify credentials that were provided by doing an API call and checking the result to verify permissions.
echo "Verifying API credentials..."
curlReturn="$(/usr/bin/curl $jamfPS/JSSResource/jssuser -i --silent --show-error --fail --user "${jamfAPIUser}:${jamfAPIPassword}" --write-out "statusCode:%{http_code}")"
# Check if the API call was successful or not.
curlCode=$(echo "$curlReturn" | /usr/bin/awk -F statusCode: '{print $2}')
if [[ $curlCode != *"200"* ]]; then
informBy "ERROR: Invalid API credentials provided!"
echo "***** verifyVPPApps process: FAILED *****"
exit 2
fi
echo "API Credentials Valid -- continuing..."
fi
# Perform the request action.
case $action in
--run | -r )
if [[ -n "${switch1}" ]]; then
outFile="${switch1}"
# Function fileExists
fileExists "${outFile}" create
# Function getAppInfo
getAppInfo
else
outFile=$(/usr/bin/osascript -e 'tell application (path to frontmost application as text)' -e 'return POSIX path of (choose file name with prompt "Provide a file name and location to save the configuration file:")' -e 'end tell' 2>/dev/null)
# Function fileExists
fileExists "${outFile}" create
# Function getAppInfo
getAppInfo
fi
;;
--help | -h | * )
# Function getHelp
getHelp
;;
esac
informBy "Script successfully completed!"
echo "***** verifyVPPApps process: COMPLETE *****"
exit 0