Remove Mobile Accounts on Login

danny_hanes
Contributor

Things I have tried:

  • LaunchAgent
    • The LaunchAgent doesn't have permissions to run the commands in the script. Wasn't sure how to get around this.
  • Policy on Login
    • We use Wifi to authenticate and when the machine attempts to check for login policies, there isn't an active connection at that time and fails.
  • Policy on Login (available offline)
    • Simply not working. Nothing in logs either
#!/bin/sh

MobileUsers=`dscl . list /Users UniqueID | awk '$2 > 1000 {print $1}'`
LocalUsers=`dscl . list /Users UniqueID | awk '$2 > 500 && $2 < 1000 {print $1}'`
CurrentUser=$(ls -l /dev/console | cut -d " " -f4)

if [[ "$CurrentUser" == "localadmin" ]]; then
    :
else
    for User in $MobileUsers ; 
        do
            if [[ "$User" == "$CurrentUser" ]]; then
                echo "No mobile users found."
            else
                sudo dscl . delete /Users/"$User"
                sudo rm -r /Users/"$User"
                echo "$(date "+%Y.%m.%d %H:%M:%S") Removing $User"  
            fi
    done
fi

This machine is a loaner. The idea is, we hand it to a user and they can log in and use the computer as much as they want. When the next user gets the machine and they log in, it will remove the account / information of the previous user.

Any help would be great.

8 REPLIES 8

mm2270
Legendary Contributor II

I would try a combination of a LaunchAgent and LaunchDaemon. Make the LaunchAgent touch a file, maybe a log or something that it can write to, which is being watched by the LaunchDaemon. Have the LaunchDaemon run the script, which will have the necessary permissions to delete accounts.

As for your script, it's not looking complete or properly formed to my eyes. What is the : in this line supposed to be doing?

if [[ "$CurrentUser" == "localadmin" ]]; then
    :

Also, why are you capturing a variable for LocalUsers when you aren't using it in the script?

danny_hanes
Contributor

The : just tells it to do nothing. If a local admin signs in, I don't want it to remove the mobile accounts. I also captured the local users just as a placeholder for now in the event I want to use it later.

I'll take a look at the launch daemon and see what I can come up with. Thanks!

millersc
Valued Contributor

You could look at Outset. That has worked great for me on login items.

mm2270
Legendary Contributor II

Here's how I would probably solve this, but it's only a suggestion.

Since it sounds like you want to not run the mobile account removal when either the local admin logs in, or the current user is determined to be the only mobile account on the system, I would use the following script in the LaunchAgent

#!/bin/bash

## Pause for 3 seconds before running, to ensure we can see who the current user is
sleep 3

loggedInUser=$(stat -f%Su /dev/console)

localAdminAccount="localadmin"

MobileUsers=$(dscl . list /Users UniqueID | awk '$2 > 1000 {print $1}' | grep -v "${loggedInUser}")

if [[ "$loggedInUser" == "$localAdminAccount" ]]; then
    echo "Local account logged in. Exiting..."
    exit 0
else
    if [ ! -z "$MobileUsers" ]; then
        printf '%s
' "$MobileUsers" > /Users/Shared/mobileaccts
    else
        echo "No accounts to remove. Exiting..."
        exit 0
    fi
fi

In the above, if the MobileUsers variable isn't empty, it's writing the results of that variable to /Users/Shared/mobileaccts, but you could write that to any other location that makes sense and where the LaunchAgent can write to. Wherever it writes to, I would then have a corresponding LaunchDaemon use that file as it's WatchPath item.
As evident, if the current user is the localadmin account, or if the results of the MobileUsers variable is empty (meaning the only mobile account is the current user), it exits, doing nothing, so the LaunchDaemon also doesn't get triggered.

The script run by the LaunchDaemon could do something like this when called:

#!/bin/bash

if [[ $(cat /Users/Shared/mobileaccts) != "" ]]; then
    while read User; do
        /usr/bin/dscl . delete /Users/"$User"
        /bin/rm -r /Users/"$User"
        echo "$(date "+%Y.%m.%d %H:%M:%S") Removing $User"
    done < <(cat /Users/Shared/mobileaccts)
else
    exit 0
fi

## Clean up the contents and prep the file to await next run
echo "" > /Users/Shared/mobileaccts
chmod 775 /Users/Shared/mobileaccts

Essentially, it reads the contents of the file that were written to that triggered the script to run, and if not empty, loop over the users placed into the list (usually will only be one account) and proceed to delete them.
Lastly, it will empty the contents of the file and change the permissions on it so the LaunchAgent will be able to write to it again next time around.

I didn't actually test any of the above, so naturally be careful to test it thoroughly if you decide to use it.

jhuls
Contributor III

As long as the users don't have admin access why not keep it simple and just use a launchagent to monitor folder changes in "/Users/"? The script ran via the launchagent would simply remove the last user and profile if it saw a new one. It seems like this would work for even non-mobile accounts if needed.

Edit: Neevermind...I reread and saw your comment about wifi authentication. That's actually an issue on my list to look into figuring out. Also it would probably have to be a launchdaemon rather than launchagent. Ugh...I need to go back to sleep.

danny_hanes
Contributor

@mm2270 Thanks for that feedback. I am going to start digging into your solution and see what I can come up with. @jhuls Yup. It's going to be fun. If I come up with anything, i'll post here as an update. 🙂

kwsenger
Contributor

@dhanes How about checking Make Available Offline within the policy?e648aea7903b4e059f1a2b6687879025
Then the policy should launch if the device is offline. We use this with some Install Cached policies.

mm2270
Legendary Contributor II

@kwsenger @dhanes already mentioned in his OP that he tried that and it wasn't working,

  • Policy on Login (available offline)
    • Simply not working. Nothing in logs either

Although I might be inclined to dig further into why that isn't working, since technically speaking, it should work. Nothing about the process should really require a connection back to the domain.