Menu Close

Inventory Update Progress with swiftDialog

Provide your users more detailed feedback when updating inventory via Jamf Pro Self Service

Background

While conducting some internal training earlier this week, one of our TSRs asked: “Is updating inventory where the blue circle just spins and spins but doesn’t appear to do anything?”

“Yes,” was my deflated reply.

Hopefully after implementing this script, you’ll never have to be asked that question again.

Overview

TL;DR

The TL;DR of this approach is to modify your existing Self Service Inventory Update policy using the script below to better provide your users with more detailed feedback.

Configuration

Complete the following steps to provide your user more detailed feedback when they update inventory via Jamf Pro Self Service.

A. Add the swiftDialog Inventory Update Progress script to your Jamf Pro server
  1. Add the swiftDialog Inventory Update Progress script to your Jamf Pro server
  2. Specify the following for Options > Parameter Labels
    • Parameter 4: Script Log Location
    • Parameter 5: Estimated Total Seconds
    • Parameter 6: Debug Mode [ true | false (default) ]
  3. Click Save

Latest version available on GitHub.

#!/bin/bash

####################################################################################################
#
#   swiftDialog Inventory Update Progress
#   https://snelson.us/2022/10/inventory-update-progress/
#
#   Purpose: Provide more detailed feedback on Jamf Pro Self Service Inventory Update
#
####################################################################################################
#
# HISTORY
#
# Version 0.0.1, 13-Oct-2022, Dan K. Snelson (@dan-snelson)
#   Original version
#
# Version 0.0.2, 14-Oct-2022, Dan K. Snelson (@dan-snelson)
#   Added logic to simply update inventory for OSes too old for swiftDialog
#
# Version 0.0.3, 18-Oct-2022, Dan K. Snelson (@dan-snelson)
#   Added "debug mode" for auditing Extension Attribute execution time
#
####################################################################################################



####################################################################################################
#
# Variables
#
####################################################################################################

scriptVersion="0.0.3"
export PATH=/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin/
loggedInUser=$( echo "show State:/Users/ConsoleUser" | scutil | awk '/Name :/ { print $3 }' )
osVersion=$( /usr/bin/sw_vers -productVersion )
osMajorVersion=$( echo "${osVersion}" | /usr/bin/awk -F '.' '{print $1}' )
dialogApp="/usr/local/bin/dialog"
dialogLog=$( mktemp /var/tmp/dialogLog.XXX )
inventoryLog=$( mktemp /var/tmp/inventoryLog.XXX )
scriptLog="${4:-"/var/tmp/org.churchofjesuschrist.log"}"
estimatedTotalSeconds="${5:-"298"}"
debugMode="${6:-"false"}"



# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# "Inventory Update" Dialog Title, Message and Icon
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #

title="Updating Inventory"
message="Please wait while inventory is updated …"
icon=$( defaults read /Library/Preferences/com.jamfsoftware.jamf.plist self_service_app_path )
inventoryProgressText="Initializing …"



# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# "Inventory Update" Dialog Settings and Features
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #

dialogInventoryUpdate="$dialogApp 
--title "$title" 
--message "$message" 
--icon "$icon" 
--mini 
--moveable 
--progress 
--progresstext "$inventoryProgressText" 
--quitkey K 
--commandfile "$dialogLog" "



# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# Validate logged-in user
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #

if [[ -z "${loggedInUser}" || "${loggedInUser}" == "loginwindow" ]]; then
    echo "No user logged-in; exiting."
    exit 0
# else
#     uid=$(id -u "${loggedInUser}")
fi



# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# Validate Operating System
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #

if [[ "${osMajorVersion}" -ge 11 ]] ; then
    echo "macOS ${osMajorVersion} installed; proceeding ..."
else
    echo "macOS ${osMajorVersion} installed; updating inventory sans progress …"
    /usr/local/bin/jamf recon -endUsername "${loggedInUser}" --verbose >> "$inventoryLog" &
    exit 0
fi



####################################################################################################
#
# Functions
#
####################################################################################################

# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# Client-side Script Logging
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #

function updateScriptLog() {
    echo -e "$( date +%Y-%m-%d %H:%M:%S )  ${1}" | tee -a "${scriptLog}"
}



# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# JAMF Display Message (for fallback in case swiftDialog fails to install)
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #

function jamfDisplayMessage() {
    updateScriptLog "Jamf Display Message: ${1}"
    /usr/local/jamf/bin/jamf displayMessage -message "${1}" &
}



# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# Check for / install swiftDialog (thanks, Adam!)
# https://github.com/acodega/dialog-scripts/blob/main/dialogCheckFunction.sh
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #

function dialogCheck(){
  # Get the URL of the latest PKG From the Dialog GitHub repo
  dialogURL=$(curl --silent --fail "https://api.github.com/repos/bartreardon/swiftDialog/releases/latest" | awk -F '"' "/browser_download_url/ && /pkg"/ { print $4; exit }")

  # Expected Team ID of the downloaded PKG
  expectedDialogTeamID="PWA5E9TQ59"

  # Check for Dialog and install if not found
  if [ ! -e "/Library/Application Support/Dialog/Dialog.app" ]; then

    updateScriptLog "Dialog not found. Installing..."

    # Create temporary working directory
    workDirectory=$( /usr/bin/basename "$0" )
    tempDirectory=$( /usr/bin/mktemp -d "/private/tmp/$workDirectory.XXXXXX" )

    # Download the installer package
    /usr/bin/curl --location --silent "$dialogURL" -o "$tempDirectory/Dialog.pkg"

    # Verify the download
    teamID=$(/usr/sbin/spctl -a -vv -t install "$tempDirectory/Dialog.pkg" 2>&1 | awk '/origin=/ {print $NF }' | tr -d '()')

    # Install the package if Team ID validates
    if [ "$expectedDialogTeamID" = "$teamID" ] || [ "$expectedDialogTeamID" = "" ]; then
 
      /usr/sbin/installer -pkg "$tempDirectory/Dialog.pkg" -target /

    else

      jamfDisplayMessage "Dialog Team ID verification failed."
      exit 1

    fi
 
    # Remove the temporary working directory when done
    /bin/rm -Rf "$tempDirectory"  

  else

    updateScriptLog "swiftDialog version $(dialog --version) found; proceeding..."

  fi

}



# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# Quit Script (thanks, @bartreadon!)
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #

function quitScript() {

    updateScriptLog "Quitting …"
    updateDialog "quit: "

    sleep 1
    updateScriptLog "Exiting …"

    # brutal hack - need to find a better way
    killall tail

    # Remove dialogLog
    if [[ -e ${dialogLog} ]]; then
        updateScriptLog "Removing ${dialogLog} …"
        rm "${dialogLog}"
    fi

    # Remove inventoryLog
    if [[ -e ${inventoryLog} ]]; then
        updateScriptLog "Removing ${inventoryLog} …"
        rm "${inventoryLog}"
    fi

    updateScriptLog "Goodbye!"
    exit 0

}



# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# Update Dialog
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #

function updateDialog() {
    echo "${1}" >> "${dialogLog}"
    sleep 0.4
}



####################################################################################################
#
# Program
#
####################################################################################################

# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# Confirm script is running as root
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #

if [[ $(id -u) -ne 0 ]]; then
  echo "This script must be run as root; exiting."
  exit 1
fi



# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# Client-side Logging
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #

if [[ ! -f "${scriptLog}" ]]; then
    touch "${scriptLog}"
    echo "$( date +%Y-%m-%d %H:%M:%S )  *** Created log file via script ***" >>"${scriptLog}"
fi



# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# Logging preamble
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #

if [[ ${debugMode} == "true" ]]; then
    updateScriptLog "DEBUG MODE | swiftDialog Inventory Update Progress (${scriptVersion})"
else
    updateScriptLog "swiftDialog Inventory Update Progress (${scriptVersion})"
fi



# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# Validate swiftDialog is installed
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #

dialogCheck



# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# Create "Inventory Update" dialog
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #

updateScriptLog "Create Inventory Update dialog …"
eval "$dialogInventoryUpdate" &

if [[ ${debugMode} == "true" ]]; then
    sleep 0.5
    updateDialog "title: DEBUG MODE | $title"
    updateDialog "message: Please wait while a DEBUG inventory is submitted …"
fi

SECONDS="0"
updateDialog "progress: 1"

/usr/local/bin/jamf recon -endUsername "${loggedInUser}" --verbose >> "$inventoryLog" &

until [[ "$inventoryProgressText" == "Submitting data to"* ]]; do

    progressPercentage=$( echo "scale=2 ; ( $SECONDS / $estimatedTotalSeconds ) * 100" | bc )
    updateDialog "progress: ${progressPercentage}"
    # if [[ ${debugMode} == "true" ]]; then
    #     updateScriptLog "DEBUG MODE | progress: ${progressPercentage}"
    # fi

    inventoryProgressText=$( tail -n1 "$inventoryLog" | sed -e 's/verbose: //g' -e 's/Found app: /System/Applications///g' -e 's/Utilities///g' -e 's/Found app: /Applications///g' -e 's/Running script for the extension attribute //g' )
    updateDialog "progresstext: ${inventoryProgressText}"
    if [[ ${debugMode} == "true" ]]; then
        updateScriptLog "DEBUG MODE | progresstext: ${inventoryProgressText}"
    fi

done



# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# Complete "Inventory Update" dialog
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #

updateScriptLog "Complete Inventory Update dialog"
updateDialog "message: Inventory update complete"
updateDialog "progress: 100"
updateDialog "progresstext: Elasped Time: $(printf '%dh:%dm:%dsn' $((SECONDS/3600)) $((SECONDS%3600/60)) $((SECONDS%60)))"
updateScriptLog "Elasped Time: $(printf '%dh:%dm:%dsn' $((SECONDS/3600)) $((SECONDS%3600/60)) $((SECONDS%60)))"

sleep 3



# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# Exit
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #

quitScript
B. Clone and Modify your Current Self Service Inventory Update Policy
  1. Clone your current Self Service Inventory Update policy
  2. Append Options > General > Display Name with (swiftDialog Progress)
  3. Remove all scripts from Options > Scripts
  4. Add the swiftDialog Inventory Update Progress script
  5. Specify the Parameter Values
    • Script Log Location (i.e., the location of your client-side logs)
    • Estimated Total Seconds (i.e., the average number of seconds an update inventory normally takes to complete in your environment)
    • Debug Mode (i.e., defaults to false; set to true if you’d like to audit Extension Attribute execution durations)
  6. Remove Maintenance > Update Inventory
  7. Click Save

Testing

To determine the correct value for Estimated Total Seconds, review the execution times of your current Self Service inventory update script and error on the upper-end of your findings.

Racing Stripes

The screencast shows the Self Service description auto-closing and Self Service auto-refreshing.

If you’d like to implement these features, please see an earlier post Refresh Self Service when users Opt-in / Opt-out of your Internal Beta Test Program.

Updates

14-Oct-2022

# Version 0.0.2, 14-Oct-2022, Dan K. Snelson (@dan-snelson)
#   Added logic to simply update inventory for OSes too old for swiftDialog

18-Oct-2022

# Version 0.0.3, 18-Oct-2022, Dan K. Snelson (@dan-snelson)
#   Added "debug mode" for auditing Extension Attribute execution time

Mac Admin Feedback

trevoredwards

Very cool! My users have made similar complaints.

Got this implemented inside 15 minutes and it looks great! 

Jamf Nation
Posted in Jamf Pro, macOS, Scripts, Tips & Tricks

Related Posts