Best way to run GUI AppleScript at login?

DanJ_LRSFC
Contributor III

We've got a GUI AppleScript which we want to run at login as the user who is logging in. It has to be saved as an .app as the AppleScript progress bar functionality does not work otherwise.

Currently we've got a policy set to trigger on login with a "Files and Processes" payload with the command open "/Library/Scripts/LongRoadInit.app" however this seems to be somewhat unreliable as we've had a number of reports of this not getting run.

We're on OS X 10.10 Yosemite and Casper Suite 9.92.

Here's the script:

set progress description to "Long Road Mac login process"
set progress additional description to "Preparing, please wait..."
set progress total steps to -1

delay 2

set progress total steps to 7
set progress completed steps to 0
set progress additional description to "Checking if Mac is bound to Active Directory"
set checkAD to do shell script "(/usr/bin/dscl localhost -list . | grep " & (quoted form of "Active Directory") & ") || echo Not found"
if checkAD contains "Active Directory" then
    -- Mac is bound to Active Directory
    set thisUser to short user name of (system info)
    set progress completed steps to 1
    set progress additional description to "Looking up user's home directory"
    set ADAccount to true
    set isStudent to false
    try
        set smbHomeRaw to do shell script "/usr/bin/dscl localhost -read /Active\ Directory/COLLEGE/All\ Domains/Users/" & thisUser & " SMBHome 2>&1"
    on error errMsg
        set progress completed steps to -1
        set progress additional description to "Not Active Directory account"
        delay 2
        set ADAccount to false
        if thisUser is equal to "student" then
            set isStudent to true
        end if
    end try
    if ADAccount then
        set studentGroups to do shell script "id " & thisUser & " | grep " & (quoted form of "All Students Security\|Examinees")
        if studentGroups as string is not equal to "" then
            set isStudent to true
        end if
        set AppleScript's text item delimiters to " "
        set the item_list to every text item of smbHomeRaw
        set the new_item to text item 2 of the item_list
        set AppleScript's text item delimiters to "\"
        set the new_item_list to every text item of new_item
        set AppleScript's text item delimiters to "/"
        set smbHome to the new_item_list as string
        if smbHome does not contain thisUser then
            set myErrorMessage to "Error! Home directory not found!"
            display alert myErrorMessage buttons {"OK"} as critical
            return
        end if
        delay 1
        set progress completed steps to 2
        set progress additional description to "Mounting user's home directory at /Users/" & thisUser & "/Documents"
        do shell script "/sbin/mount -t smbfs " & smbHome & " /Users/" & thisUser & "/Documents"
        if (list disks) does not contain thisUser then
            set myErrorMessage to "Error! Could not mount home directory!"
            display alert myErrorMessage buttons {"OK"} as critical
            return
        end if
    end if
else
    set progress completed steps to -1
    set progress additional description to "Not bound to Active Directory"
    delay 10
    return
end if
if isStudent then
    set progress completed steps to 3
    set progress additional description to "Configuring Dock icons"
    set dockUtilPath to "/Library/Scripts/LongRoadInit.app/Contents/Resources/Scripts/dockutil.py"
    set userPath to "/Users/" & thisUser
    set duNoRestart to " --no-restart"
    set duAdd to " --add "
    set duSectionApps to " --section apps "
    set duSectionOthers to " --section others "
    do shell script dockUtilPath & duNoRestart & " --remove all " & userPath
    do shell script dockUtilPath & duNoRestart & duAdd & (quoted form of "/Applications/Google Chrome.app") & duSectionApps & userPath
    do shell script dockUtilPath & duNoRestart & duAdd & (quoted form of "/Applications/VLC.app") & duSectionApps & userPath
    do shell script dockUtilPath & duNoRestart & duAdd & (quoted form of "/Applications/Image Capture.app") & duSectionApps & userPath
    do shell script dockUtilPath & duNoRestart & duAdd & (quoted form of "/Applications/Adobe Bridge CS6/Adobe Bridge CS6.app") & duSectionApps & userPath
    do shell script dockUtilPath & duNoRestart & duAdd & (quoted form of "/Applications/Adobe Illustrator CS6/Adobe Illustrator.app") & duSectionApps & userPath
    do shell script dockUtilPath & duNoRestart & duAdd & (quoted form of "/Applications/Adobe Photoshop CS6/Adobe Photoshop CS6.app") & duSectionApps & userPath
    do shell script dockUtilPath & duNoRestart & duAdd & (quoted form of "/Applications/Adobe After Effects CS6/Adobe After Effects CS6.app") & duSectionApps & userPath
    do shell script dockUtilPath & duNoRestart & duAdd & (quoted form of "/Applications/Adobe Premiere Pro CS6/Adobe Premiere Pro CS6.app") & duSectionApps & userPath
    do shell script dockUtilPath & duNoRestart & duAdd & (quoted form of "/Applications/Adobe InDesign CS6/Adobe InDesign CS6.app") & duSectionApps & userPath
    set scriptCommand to "if [[ -d " & (quoted form of "/Applications/Autodesk/AutoCAD 2015") & " ]];then " & dockUtilPath & duNoRestart & duAdd & (quoted form of "/Applications/Autodesk/AutoCAD 2015/AutoCad 2015.app") & duSectionApps & userPath & ";fi"
    do shell script scriptCommand
    do shell script dockUtilPath & duNoRestart & duAdd & (quoted form of "/Applications/Autodesk/maya2016/Maya.app") & duSectionApps & userPath
    do shell script dockUtilPath & duNoRestart & duAdd & (quoted form of "/Applications/Autodesk/Mudbox2016/Mudbox.app") & duSectionApps & userPath
    do shell script dockUtilPath & duNoRestart & duAdd & (quoted form of "/Applications/Microsoft Office 2011/Microsoft Word.app") & duSectionApps & userPath
    do shell script dockUtilPath & duNoRestart & duAdd & (quoted form of "/Applications/Microsoft Office 2011/Microsoft Excel.app") & duSectionApps & userPath
    do shell script dockUtilPath & duNoRestart & duAdd & (quoted form of "/Applications/Microsoft Office 2011/Microsoft PowerPoint.app") & duSectionApps & userPath
    do shell script dockUtilPath & duNoRestart & duAdd & (quoted form of "/Applications/PCClient.app") & duSectionApps & userPath
    delay 1
    set progress completed steps to 4
    set progress additional description to "Restarting Dock"
    do shell script dockUtilPath & duAdd & (quoted form of ("/Users/" & thisUser & "/Documents")) & " --label " & (quoted form of thisUser) & duSectionOthers & userPath
    delay 1
    set progress completed steps to 5
    set progress additional description to "Resetting wallpaper"
    tell application "Finder" to set desktop picture to POSIX file "/Library/Desktop Pictures/lrsfc_lrcstudents_wallpaper_mac.jpg"
    delay 1
end if
set progress completed steps to 6
set progress additional description to "Loading PaperCut client"
delay 1
tell application "System Events"
    if exists file "/Applications/PCClient.app" then
        do shell script "open -g /Applications/PCClient.app"
    end if
end tell
delay 1
set progress completed steps to 7
set progress additional description to "Login complete, you may now start using the Mac"
delay 2
12 REPLIES 12

davidacland
Honored Contributor II

Hi,

The login trigger still uses loginhooks (I believe) which runs as root and too early for AppleScripts.

Have you tried triggering it with a LaunchAgent instead?

Last resort would be to add something to the users login items either with another script or a profile.

millersc
Valued Contributor

LaunchAgent has been working for use with an AppleScript that mounts User shares. You could also look into outset. Been testing that with Dockutil.

DanJ_LRSFC
Contributor III

@davidacland We used to run it with a LaunchAgent, however we found that there was often a considerable delay between the user logging in and the script running.

Is there another way we can display either progress or status messages on the screen for the user while our script is executing? That way we could remove the AppleScript requirement.

With regard to a script running as root, is there a way using sudo to run commands in the logged in user's context? Specifically the mount command so that we don't need to know the user's password in order to connect to their network home folder.

Thanks,
Dan Jackson (Lead ITServices Technician)
Long Road Sixth Form College
Cambridge, UK.

davidacland
Honored Contributor II

You could try using jamfhelper to display a message while it is running.

DanJ_LRSFC
Contributor III

@davidacland The full screen mode of that would be great if it wasn't modal (i.e. allowed my script to continue in the background) or respected the timeout option (it doesn't seem to). Or am I using it wrong?

Ideally rather than a static message I'd like to be able to change what is displayed as the script goes along, so that the user knows something is happening.

EDIT: looks like something like this will work:

#!/bin/bash

/Library/Application Support/JAMF/bin/jamfHelper.app/Contents/MacOS/jamfHelper -windowType fs -heading "Long Road Login" -description "Preparing login, please wait..." &
JAMF_PID=$!
# do stuff
kill $JAMF_PID
/Library/Application Support/JAMF/bin/jamfHelper.app/Contents/MacOS/jamfHelper -windowType fs -heading "Long Road Login" -description "Some other message..." &
JAMF_PID=$!
# do more stuff
kill $JAMF_PID

Look
Valued Contributor III

One thing you can do is use two overlaying Jamfhelper windows that read the last line of a file, then you can just have other processes append stuff to the file to update the message.
This is the loop I have as part of a post restart status message script, it has no exit clause as the machine restarts again, but it might give you some ideas. basically the new window appears over the previous one and the previous one times out in the background. It works reasonably well but you kind of have to cleanse the messages etc... so they are similar lengths.

while :
do
Status_Mark=$(date +"%H:%M:%S")
Status_Message=$(tail -n1 /Library/Logs/JAMF/ImagingScripts.log)
/Library/Application Support/JAMF/bin/jamfHelper.app/Contents/MacOS/jamfHelper -title "Please do not use or power off" -heading "STATUS   ${Status_Mark}" -description "${Status_Message}" -lockHUD -timeout 10 -windowType utility -windowPosition ll &
sleep 9
done

DanJ_LRSFC
Contributor III

So here's what I've got so far.

EDIT: To mitigate the flashing, I found if I stack up all the messages in reverse order at the start and then kill them off one by one, that achieves a message change without the flash.

#!/bin/bash

# Get logged in user, the official Apple-approved way
loggedInUser=`python -c 'from SystemConfiguration import SCDynamicStoreCopyConsoleUser; import sys; username = (SCDynamicStoreCopyConsoleUser(None, None, None) or [None])[0]; username = [username,""][username in [u"loginwindow", None, u""]]; sys.stdout.write(username + "
");'`
echo "Logged in user is $loggedInUser"

# Stack jamfHelper windows up to start with, kill processes when you want to change message
/Library/Application Support/JAMF/bin/jamfHelper.app/Contents/MacOS/jamfHelper -windowType fs -heading "Long Road Mac Login" -description "Login complete, you may now start using the Mac." &
PID_LOGINCOMPLETE=$!
/Library/Application Support/JAMF/bin/jamfHelper.app/Contents/MacOS/jamfHelper -windowType fs -heading "Long Road Mac Login" -description "Loading PaperCut client..." &
PID_PAPERCUT=$!
/Library/Application Support/JAMF/bin/jamfHelper.app/Contents/MacOS/jamfHelper -windowType fs -heading "Long Road Mac Login" -description "Resetting wallpaper..." &
PID_WALLPAPER=$!
/Library/Application Support/JAMF/bin/jamfHelper.app/Contents/MacOS/jamfHelper -windowType fs -heading "Long Road Mac Login" -description "Restarting Dock..." &
PID_DOCK_RESTART=$!
/Library/Application Support/JAMF/bin/jamfHelper.app/Contents/MacOS/jamfHelper -windowType fs -heading "Long Road Mac Login" -description "Configuring Dock icons..." &
PID_DOCK_CONFIG=$!
/Library/Application Support/JAMF/bin/jamfHelper.app/Contents/MacOS/jamfHelper -windowType fs -heading "Long Road Mac Login" -description "Mounting user's home directory at /Users/$loggedInUser/Documents..." &
PID_MOUNT=$!
/Library/Application Support/JAMF/bin/jamfHelper.app/Contents/MacOS/jamfHelper -windowType fs -heading "Long Road Mac Login" -description "Looking up user's home directory..." &
PID_LOOKUP=$!
/Library/Application Support/JAMF/bin/jamfHelper.app/Contents/MacOS/jamfHelper -windowType fs -heading "Long Road Mac Login" -description "This Mac is not bound to Active Directory." &
PID_NOAD=$!
/Library/Application Support/JAMF/bin/jamfHelper.app/Contents/MacOS/jamfHelper -windowType fs -heading "Long Road Mac Login" -description "Checking if Mac is bound to Active Directory..." &
PID_CHECKAD=$!
/Library/Application Support/JAMF/bin/jamfHelper.app/Contents/MacOS/jamfHelper -windowType fs -heading "Long Road Mac Login" -description "Preparing login, please wait..." &
PID_PREPARING=$!

sleep 2
kill $PID_PREPARING
wait $PID_PREPARING 2> /dev/null
checkAD=`/usr/bin/dscl localhost -list . | grep "Active Directory"`
if [ "${checkAD}" != "Active Directory" ]
then
    # Mac is not bound to Active Directory
    kill $PID_CHECKAD
    wait $PID_CHECKAD 2> /dev/null
    sleep 2
    killall jamfHelper
    exit 2
fi
kill $PID_NOAD
wait $PID_NOAD 2> /dev/null
kill $PID_CHECKAD
wait $PID_CHECKAD 2> /dev/null
ADAccount="true"
isStudent="false"
smbHomeRaw=`/usr/bin/dscl localhost -read "/Active Directory/COLLEGE/All Domains/Users/$loggedInUser" SMBHome 2>&1`
if [ $? != 0 ]
then
    # Not an Active Directory account
    ADAccount="false"
    if [ "$loggedInUser" == "student" ]
    then
        isStudent="true"
    fi
fi
if [ "$ADAccount" == "true" ]
then
    # Active Directory account
    studentGroups=`id $loggedInUser | grep "All Students Security|Examinees"`
    if [ "$studentGroups" != "" ]
    then
        isStudent="true"
    fi
    # Munge SMB Home into correct format
    smbHome="smb:"
    smbHome+=`echo $smbHomeRaw | awk '{ print $2 }' | sed 's.\\./.g'`
    kill $PID_LOOKUP
    wait $PID_LOOKUP 2> /dev/null
    echo "smbHome is $smbHome"
    sudo -u $loggedInUser /sbin/mount -t smbfs "$smbHome" "/Users/$loggedInUser/Documents"
fi
if [ `pgrep jamfHelper | grep $PID_LOOKUP` ]
then
    kill $PID_LOOKUP
    wait $PID_LOOKUP 2> /dev/null
fi
if [ "$isStudent" == "true" ]
then
    # do stuff if the account is a student
    # dock icons
    # wallpaper
    MAC_MAJOR_VERSION=`sw_vers -productVersion | awk -F '.' '{print $1 "." $2}'`
    echo "Checking OS version for dockutil: $MAC_MAJOR_VERSION"
    if [ "$MAC_MAJOR_VERSION" == "10.8" ]
    then
        echo "dockutil 1.1.4"
        dockUtilPath="/Library/Scripts/dockutil.1.1.4.py"
    else
        echo "dockutil 2.0.4"
        dockUtilPath="/Library/Scripts/dockutil.2.0.4.py"
    fi
    userPath="/Users/$loggedInUser"
    duNoRestart="--no-restart"
    duAdd="--add"
    duSectionApps="--section apps"
    duSectionOthers="--section others"
    kill $PID_MOUNT
    wait $PID_MOUNT 2> /dev/null
    echo "Removing all Dock icons"
    $dockUtilPath $duNoRestart --remove all $userPath
    sleep 2
    echo "Adding new Dock icons"
    $dockUtilPath $duNoRestart $duAdd "/Applications/Google Chrome.app" $duSectionApps $userPath
    $dockUtilPath $duNoRestart $duAdd "/Applications/VLC.app" $duSectionApps $userPath
    $dockUtilPath $duNoRestart $duAdd "/Applications/Image Capture.app" $duSectionApps $userPath
    #$dockUtilPath $duNoRestart $duAdd "/Applications/Adobe Bridge CS6/Adobe Bridge CS6.app" $duSectionApps $userPath
    #$dockUtilPath $duNoRestart $duAdd "/Applications/Adobe Illustrator CS6/Adobe Illustrator.app" $duSectionApps $userPath
    #$dockUtilPath $duNoRestart $duAdd "/Applications/Adobe Photoshop CS6/Adobe Photoshop CS6.app" $duSectionApps $userPath
    #$dockUtilPath $duNoRestart $duAdd "/Applications/Adobe After Effects CS6/Adobe After Effects CS6.app" $duSectionApps $userPath
    #$dockUtilPath $duNoRestart $duAdd "/Applications/Adobe Premiere Pro CS6/Adobe Premiere Pro CS6.app" $duSectionApps $userPath
    #$dockUtilPath $duNoRestart $duAdd "/Applications/Adobe InDesign CS6/Adobe InDesign CS6.app" $duSectionApps $userPath
    if [ -d "/Applications/Autodesk/AutoCAD 2015" ]
    then
        $dockUtilPath $duNoRestart $duAdd "/Applications/Autodesk/AutoCAD 2015/AutoCad 2015.app" $duSectionApps $userPath
    fi
    $dockUtilPath $duNoRestart $duAdd "/Applications/Autodesk/maya2016/Maya.app" $duSectionApps $userPath
    $dockUtilPath $duNoRestart $duAdd "/Applications/Autodesk/Mudbox2016/Mudbox.app" $duSectionApps $userPath
    $dockUtilPath $duNoRestart $duAdd "/Applications/Microsoft Office 2011/Microsoft Word.app" $duSectionApps $userPath
    $dockUtilPath $duNoRestart $duAdd "/Applications/Microsoft Office 2011/Microsoft Excel.app" $duSectionApps $userPath
    $dockUtilPath $duNoRestart $duAdd "/Applications/Microsoft Office 2011/Microsoft PowerPoint.app" $duSectionApps $userPath
    $dockUtilPath $duNoRestart $duAdd "/Applications/PCClient.app" $duSectionApps $userPath
    kill $PID_DOCK_CONFIG
    wait $PID_DOCK_CONFIG 2> /dev/null
    echo "Adding Documents folder and restarting Dock"
    $dockUtilPath $duAdd "$userPath/Documents" --label $loggedInUser $duSectionOthers $userPath
    kill $PID_DOCK_RESTART
    wait $PID_DOCK_RESTART 2> /dev/null
    echo "Resetting wallpaper"
    osascript -e 'tell application "Finder" to set desktop picture to POSIX file "/Library/Desktop Pictures/lrsfc_lrcstudents_wallpaper_mac.jpg"'
fi
kill $PID_WALLPAPER
wait $PID_WALLPAPER 2> /dev/null
if [ `pgrep jamfHelper | grep $PID_MOUNT` ]
then
    kill $PID_MOUNT
    wait $PID_MOUNT 2> /dev/null
fi
if [ `pgrep jamfHelper | grep $PID_DOCK_CONFIG` ]
then
    kill $PID_DOCK_CONFIG
    wait $PID_DOCK_CONFIG 2> /dev/null
fi
if [ `pgrep jamfHelper | grep $PID_DOCK_RESTART` ]
then
    kill $PID_DOCK_RESTART
    wait $PID_DOCK_RESTART 2> /dev/null
fi
if [ -d "/Applications/PCClient.app" ]
then
    open -g /Applications/PCClient.app
fi
kill $PID_PAPERCUT
wait $PID_PAPERCUT 2> /dev/null
sleep 2
kill $PID_LOGINCOMPLETE
wait $PID_LOGINCOMPLETE 2> /dev/null
exit 0

mm2270
Legendary Contributor III
I don't suppose there's any way to tell an existing/running jamfHelper process to change its message?

No there isn't. Its a static image and message per call. The only way to change it on the fly is to do similar to what you're doing. killing one and popping up another, repeatedly. This boils down to using jamfHelper for a purpose different than how JAMF intended it to be used really. Not that that's a bad thing, but its just the reality of the situation and something you need to live with for now, until or if JAMF beefs up jamfHelper's capabilities. I'm not sure that that's on their roadmap though.

DanJ_LRSFC
Contributor III

My next problem is, occasionally when I log in, this script doesn't run at all. How do I troubleshoot this? The logs in the policy don't shed any light on it. Is it just that the policy needs to be made available offline or something?

mm2270
Legendary Contributor III

You could set it to run Offline, but, and a lot of people get confused by this, there's a bit of a chicken and egg problem with offline cached policies. The policy must run at least once while the Mac logs in "online" or on a network that is connected to the JSS in order for it to be cached to the Mac to later run in offline mode. If you have Macs that have never run it before, booting up and logging in off the network, it won't run on them. Its not going to until some later time when the client logs in with an active network connection so it can run the Login policy trigger successfully.
This is one of the main reasons I almost never use the login trigger with policies. It may be OK in labs or with iMacs and Mac Pros that are typically using a wired Ethernet connection, but as we are about 99% laptops, so many of our Macs log in off the network, so our success rate in using a login trigger would be pretty dismal.

Outside of this, have you checked to make sure the execution frequency is set correctly? Is it set to run once per computer or on some other frequency?

DanJ_LRSFC
Contributor III

@mm2270 I am seeing this problem on my test Mac though, which is connected with a wired Ethernet connection.

I set the execution frequency to Ongoing as we want it to run every time any user logs on. I have not set any limitations or exclusions, but as this is just for testing purposes, the target is set to All Users and Specific Computers with my test Mac added in.

bentoms
Release Candidate Programs Tester

@DanJ_LRSFC Could this script be triggered via a LaunchAgent instead?