Remove old mobile accounts.

AndyBeaver
Contributor II

I am needing a script that can be added to our summer update configuration that will delete every user with the exception of admins. Anyone had any luck doing this? I have tried using Tom Larkin's script and it seems to remove the home directories from /Users how ever the accounts are still populated in SystemPreferences>Accounts. Any ideas?

https://discussions.apple.com/message/12165862#12165862

dscl . -delete /Users/


dscl . list /Users | grep -v "_"


#!/bin/bash

UserList=`/bin/ls /Users | /usr/bin/grep -v "Shared"`

for u in $UserList ; do

if [[ `/usr/bin/dscl . read /Groups/admin GroupMembership | /usr/bin/grep $u -c` == 1 ]]

then /bin/echo "Admin account detected skipping..."

else /usr/bin/dscl . delete /Users/$u && /bin/rm -rf /Users/$u

fi

done
1 ACCEPTED SOLUTION

cbrewer
Valued Contributor II

The "dscl . delete /Users/username" command should remove it from the Accounts prefpane.

My script builds a list of users with UniqueID's higher than 1000 (this way it's only looking at cached AD users). It then checks for home directories older than 21 days and then removes the account and home directory for that user. It also only deletes users who are members of our students group in AD.

#!/bin/sh

userList=`dscl . list /Users UniqueID | awk '$2 > 1000 {print $1}'`

echo "Deleting account and home directory for the following users..."

for a in $userList ; do
    if [[ "$(id $a | tr '[:upper:]' '[:lower:]')" =~ "your student AD group" ]]; then
        find /Users -type d -maxdepth 1 -mindepth 1 -not -name "*.*" -mtime +21 | grep "$a"
        if [[ $? == 0 ]]; then
            dscl . delete /Users/"$a"  #delete the account
            rm -r /Users/"$a"  #delete the home directory
        fi
    fi
done

View solution in original post

63 REPLIES 63

nextyoyoma
Contributor

That looks like it should work. The only reason it would not (that I can see) is if the home folders do not have the same name as the actual user account. E.g., if the short name is nextyoyoma but the home folder is named charlie, this would not delete the directory entry for the user.

If you go to terminal and run:

dscl . list /Users | grep "insert username"

EDIT: My brain totally skipped over the first two lines before the shebang. Those probably won't do anything, but it would be a good idea to take that out. In other words, the script really starts at:

#!/bin/bash

you shouldn't get any output if the script worked correctly. If you get no output, try a quick restart and see if the entries go away. If you get the username output, the script didn't work for some reason. You can try:

sudo bash -x /path/to/script to see the result of every line and maybe find out what is going wrong.

AndyBeaver
Contributor II

I ran the script again with as you suggested and it does indeed successfully remove the home folder for each user, however even after a restart the names of the deleted users are still populated in the Accounts prefPane. When you choose to remove one the OS does NOT prompt you to delete their home folder nor ask if you want to save it as a dmg. It just simply deletes. I have tried removing a few plists for SystemPreferences as well, still no change in the accounts prefpane.

AndyBeaver
Contributor II

dscl . list /Users gives me a list of the same users populated in the Accounts prefpane as well.

nextyoyoma
Contributor

are these local users or are they active directory/open directory accounts?

btw, running the script again will not accomplish anything if there are no home folders in the /Users/ folder. The reason is that the script relies on the list of users found in that folder to know what to delete.

AndyBeaver
Contributor II

these are AD users

nextyoyoma
Contributor

Ok, I can tell you that is most likely the problem. I don't know enough about active directory to tell you what to do at this point, but I believe this script is only deleting users in the local domain.

nextyoyoma
Contributor

Ok, I can tell you that is most likely the problem. I don't know enough about active directory to tell you what to do at this point, but I believe this script is only deleting users in the local domain.

AndyBeaver
Contributor II

You know and thats ok, the main goal behind this is to free up space and clutter! I appreciate your help. Ill leave this thread open incase someone know how to remove those accounts from the Accounts prefpane. Again thank you!

cbrewer
Valued Contributor II

The "dscl . delete /Users/username" command should remove it from the Accounts prefpane.

My script builds a list of users with UniqueID's higher than 1000 (this way it's only looking at cached AD users). It then checks for home directories older than 21 days and then removes the account and home directory for that user. It also only deletes users who are members of our students group in AD.

#!/bin/sh

userList=`dscl . list /Users UniqueID | awk '$2 > 1000 {print $1}'`

echo "Deleting account and home directory for the following users..."

for a in $userList ; do
    if [[ "$(id $a | tr '[:upper:]' '[:lower:]')" =~ "your student AD group" ]]; then
        find /Users -type d -maxdepth 1 -mindepth 1 -not -name "*.*" -mtime +21 | grep "$a"
        if [[ $? == 0 ]]; then
            dscl . delete /Users/"$a"  #delete the account
            rm -r /Users/"$a"  #delete the home directory
        fi
    fi
done

AndyBeaver
Contributor II

Works like a charm thank you. I need to learn more about awk!

tlarkin
Honored Contributor

Hey everyone,

What Chad Brewer outlined is what I use personally when trying to scope out data in scripts by checking a UID of a user account. If it is a mobile AD account, it will create a local account and give it a UID of greater than 1,000.

If you don't use the mobile account option in your AD binding, you can also have dscl look at the LDAP path of your server and do the same thing.

My opinion (not fact) is that any sort of local admin that is used for internal IT use should be hidden and have a UID of under 500 so whenever you need to add/move/modify users/groups you can do so with out touching your local internal IT admin accounts.

thanks,
Tom

rfreeborn
New Contributor III

When I run Tom's script from JSS I get the following errors in policy log. It does however remove the non-admin mobile accounts and home directory from my test machines. Is this a problem with script or am I missing something?

Script Result: <dscl_cmd> DS Error: -14009 (eDSUnknownNodeName)
delete: Invalid Path
<dscl_cmd> DS Error: -14009 (eDSUnknownNodeName)
delete: Invalid Path

tlarkin
Honored Contributor

Hey Rick,

By default dscl looks to the local path for users. So, any mobile account or local account that exists in the local BSD database will be found with my previous script I wrote there. If you the accounts you are trying to remove aren't mobile accounts or local you would have to pump in the full LDAP path into the dscl command.

Does that help you?

thanks,
Tom

rfreeborn
New Contributor III

Hey Tom,

They are mobile accounts and per my post the script did remove the non-admin accounts and home directories but the error in the log concerned me. I did some looking around and it appears the two errors were generated from the local admin and mobile admin as the script identifies them and the error relates to Missing Group Identifiers but I verified on other system that they aren't missing by doing
dscl . -read /Groups/admin GroupMembership and both accounts show as part of admin Group Membership. So definitely not a problem with Group Identifier that I can tell. If I make the admin accounts hidden which I agree with you they should be, I don't get the error.

rockpapergoat
Contributor III

tom, that last post was confusing. dscl uses the path you specify, not any specific path by default. when you use "." to point to the local default node, that's where it will operate. you could use 'dscl /Search' to use any path in the host's search path without specifying the directory node. in this case, that's not necessary, as any mobile account (if it really is mobile) will show up as a normal account in the local node. if you're looking to remove non-local directory accounts, i'd say you shouldn't do it from a client.

a more robust way to do this is to query only the local directory service for these users and grab the homedirs from these account records. iterating over a file listing of /Users is flawed. i have something that does this already and will post in a bit. trying to get to work at the moment…

CasperSally
Valued Contributor II

I'm trying to get this script to work to delete mobile accounts older than 2 days. I know I'm probably missing something stupid, in my testing (Lion), it's deleting all mobile accounts regardless of age.

Can anyone help me figure out what I'm doing wrong? I took out the AD check line from cbrewer's script above.

#!/bin/sh

userList=`dscl . list /Users UniqueID | awk '$2 > 1000 {print $1}'`

echo "Deleting account and home directory for the following users..."

for a in $userList ; do
find /Users -type d -maxdepth 1 -mindepth 1 -not -name "*.*" -mtime +2 | grep "$a"

if [[ $? == 0 ]]; then
dscl . delete /Users/"$a"  #delete the account
rm -r /Users/"$a"  #delete the home directory
fi

done

The script outputs showing only the account older than 2 days being deleted, but when I look at users folder and system prefs, the mobile account created only 1 hour old was also deleted?

nessts
Valued Contributor II

why not use the built in stuff to delete accounts?
look under Managed Preferences aka MCX on your management page and check out the mobile account time to live

CasperSally
Valued Contributor II

We use MCX expiry for Snow Leopard, it's not working in Lion 10.7.4 in my testing.

I have a case in with Apple on it. If anyone has MCX account clearing working in Lion I'd prefer that?

cbrewer
Valued Contributor II

Sally, what happens when you just run "find /Users -type d -maxdepth 1 -mindepth 1 -mtime +2" from terminal? If your script output only shows the accounts older than 2 days, I'm not sure how it would be deleting other accounts as well. You sure some other process didn't remove the account?

CasperSally
Valued Contributor II

cbrewer,

Thanks for replying. I think you helped me find the issue. When I run
"find /Users -type d -maxdepth 1 -mindepth 1 -mtime +2"

It only returns the one mobile account older than 2 days (teststudent). My other test account was just added to this computer today (test) wasn't returned.

But, when I run the script, both accounts were again deleted. It seems like the issue is the similarity of names on my test accounts.

When I run the script, what's returned is "deleting home dir and account for following users... /users/teststudent and then another line for /users/teststudent"

I logged in with a 3rd account (not similar in name) and ran the script again, and it left the 3rd test student account this time properly. Our students follow a convention that I don't think in production this would be an issue for us but will continue to test.

ShawnIT
New Contributor

Im attempting to use cbrewer's script on a 10.10 machine. I get the message that "Deleting account and home directory for the following users..." but no accounts are removed. Has anyone else ran into that?

cbrewer
Valued Contributor II

@ShawnIT I Just gave it a try on 10.10 and had no issues. You sure the computer you are using it on has user accounts that meet the criteria? It'll echo out "Deleting account and home directory for the following users..." even if there's no accounts to actually remove.

ShawnIT
New Contributor

@cbrewer][/url I can run the dscl . list /Users UniqueID | awk '$2 > 1000 {print $1}' line in terminal and it returns about 30 users. Im sure I am just making a simple type somewhere. Here is what I copied and pasted into my editor.

#!/bin/sh

userList=`dscl . list /Users UniqueID | awk '$2 > 1000 {print $1}'`

echo "Deleting account and home directory for the following users..."

for a in $userList ; do
if [[ "$(id $a | tr '[:upper:]' '[:lower:]')" = "bls students" ]]; then
find /Users -type d -maxdepth 1 -mindepth 1 -not -name "*.*" -mtime +21 | grep "$a"
if [[ $? == 0 ]]; then
dscl . delete /Users/"$a" #delete the account
rm -r /Users/"$a" #delete the home directory
fi
fi
done

cbrewer
Valued Contributor II

@ShawnIT Unless your users are members of a group called "bls students", I'd remove or change that line. That was an example from my environment.

ShawnIT
New Contributor

@cbrewer Wow I am sorry about that I looked at that on and off for hrs today. Its been that kind of day for me everyting has been backwards for me. Works perfect! Thanks for you help!!

GabeShack
Valued Contributor III

Just to clarify since I'm low on sleep these days,
This does not delete local accounts on the computer since its scoped to users with a UID over 1000 correct?

If I take out the line for

if [[ "$(id $a | tr '[:upper:]' '[:lower:]')" =~ "bls students" ]]; then

And change the days to 30, this should essentially just delete Moblie AD accounts older than 30 days correct?

Also does this leave in place admin AD Mobile accounts?

Gabe Shackney
Princeton Public Schools

Gabe Shackney
Princeton Public Schools

tlarkin
Honored Contributor

Hi Everyone,

I just got notified this thread was updated via email. I was recently doing some workflows around creation date of user accounts with some people who wanted to scrub out local accounts after a certain date. This was all tested in Yosemite, so I do not know how directory services is different in each version of OS X.

To get a user account's creation date you can run this:

dscl . readpl /Users/tlarkin accountPolicyData creationTime | awk -F: '{ print $2 }' | tr -d " " | cut -d "." -f 1

This will return the date the account was created in Epoch time

You can see how old it is with a little math.

todayEpoch=$(date "+%s")
creationDate=$(dscl . readpl /Users/tlarkin accountPolicyData creationTime | awk -F: '{ print $2 }' | tr -d " " | cut -d "." -f 1)

daysOld=$(expr ${todayEpoch} - ${creationDate})

From here you can simply write some logic into your script to see how many days the account has existed, and set your integer for expiration. So, if you wanted to expire accounts every 90 or 180 days you could simply test to see if the value is greater than either of those integers, and then take action.

This is just one way to do it. If these are AD accounts you will have to query the AD record from dscl, and do a bit more math since I think AD uses a different time format and doesn't use standard Epoch time.

Hope this helps,
Tom

cbrewer
Valued Contributor II

@gshackney

You'd definitely want to take out or modify that line. Also, take note that of the -not -name "." part. I'm using that to skip any accounts that have a period in them. You'll probably want to modify that as well.
Also, this will most likely remove admin AD users unless you do something to exempt them.

GabeShack
Valued Contributor III

One thing I'm seeing is if this script runs it keeps the deleted users from logging in again (with an error) until the machine is rebooted.

Otherwise its working perfectly! Thanks!

Gabe Shackney
Princeton Public Schools

Gabe Shackney
Princeton Public Schools

CasperSally
Valued Contributor II

Frustrating the Config Profile setting to delete mobile accounts after X days is broken in 10.10.3 - worked great in 10.9.4.

I used the script above for the same bug in 10.8.4 I think it as back in 2012.

Back to testing this script again for use next school year. thankful this post still exists!

lastoch
New Contributor

My district uses S####### naming schema for the students and a mix of E######## and older user names for the staff. I have modified the script to delete all of the student accounts from the system, it works on both LDAP and AD mobile accounts.

Chris Lastovica
Casper Admin Austin ISD
Austin Texas

#!/bin/sh
userList=`dscl . list /Users UniqueID | awk '$2 > 1000 {print $1}'`

echo "Deleting account and home directory for the following users..."

for a in $userList ; do
find /Users -type d -maxdepth 1 -mindepth 1 -name "S[0-9]*" | grep "$a"

if [[ $? == 0 ]]; then
dscl . delete /Users/"$a"  #delete the account
rm -r /Users/"$a"  #delete the home directory
fi

done

Sanchi
Contributor

lastoch This worked for my in my Yosemite 10.10.5 environment. Our University uses the u####### naming scheme for student IDs and this worked charm. It deletes student accounts from Users and Groups as well as the the home directory while leaving staff accounts and admin accounts alone. I simply changed your 'S' to a 'u' to suit my needs. Cheers.

Sean_Ginn
New Contributor II

Hey guys I know this hasn't been posted on for sometime, but I am running into an issue that I can't seem to get passed. Our fleet is currently running 10.11, and the script appears to run, displays the echo command but then doesn't show what has been deleted and doesn't appear to be doing anything. I have changed the section where it requires a group membership and I still am not getting any idea what is wrong.

Is there a way to get the top half of this to run on it's own, just to see what it is returning before it gets passed to the deletion section? Is there a to see what the require group needs to look like?

Thanks for the support and sorry for reviving an old post.

mm2270
Legendary Contributor III

@Sean_Ginn Considering there's at least 4 variations of this script posted on the thread, it may be a good idea to post the version you're using so someone can examine it and help out. I suspect some under the hood changes in 10.11 are causing the issue, but I think seeing what you're working with would be best to start with.

Sean_Ginn
New Contributor II

@mm2270 Sorry I didn't even think about that posting that. The script that I am using is the first one posted by @cbrewer because I love the idea of being able to remove just student accounts and leaving teacher accounts alone, and doing this based on their group membership. Thank you again for the support.

#!/bin/sh

userList=`dscl . list /Users UniqueID | awk '$2 > 1000 {print $1}'`

echo "Deleting account and home directory for the following users..."

for a in $userList ; do
    if [[ "$(id $a | tr '[:upper:]' '[:lower:]')" =~ "bls students" ]]; then
        find /Users -type d -maxdepth 1 -mindepth 1 -not -name "*.*" -mtime +21 | grep "$a"
        if [[ $? == 0 ]]; then
            dscl . delete /Users/"$a"  #delete the account
            rm -r /Users/"$a"  #delete the home directory
        fi
    fi
done

Sean_Ginn
New Contributor II

Quick update...

I was able to get it working by removing

#!/bin/sh
if [[ "$(id $a | tr '[:upper:]' '[:lower:]')" =~ "bls students" ]]; then
...
fi

Any ideas on how to get this working? Right now I have to be restrictive with where this script will go as it can't go on teacher machines. While this isn't a big deal, but i'd like to remove any and all stale student accounts across our district. I have changed out "bls students" for our group name and it doesn't seem to be able to correctly differentiate and just ignores all of the users.

cbrewer
Valued Contributor II

@Sean_Ginn

The line you removed is what checks to see if the user is a member of a certain AD group. In my case "bls students" is what I wanted to check against. If you have a different student AD group, just substitute the name.

Sean_Ginn
New Contributor II

@cbrewer Thanks for the response, I was able to gather that information from the earlier posts, but I was running into an issue getting it to run with our current group name, because I am for lack of a better word a complete and total noob when it comes to shell scripting. I'm pretty good at reading and deciphering most of it and put it to use, but this section totally evaded me and looks a little bit like Swahili. I changed out "bls students" with our AD group name, but it didn't seem to do anything and just displayed the text from the Echo command. So I was able to remove it and it ran like a champ.

Now the issue i'm running into is how to deal with users who aren't mobile accounts, but are forced to create local home directories on the computer when they login with their AD creds. I was able to write a workaround with the following line...

#!/bin/sh
find /Users -type d -maxdepth 1 -mindepth 1 -not -name "*.*" -not -name "Shared" -not -name "hadmin" -mtime +21 -exec rm -rf {} ;

Which will obviously delete their user folders (since these users don't exist in the Users and Groups section of System Preferences) that are older than 21 days, but it doesn't have the beauty of yours where it is able to use dscl to determine the UUID, separate those users into student and teacher account, and only remove the ones that are 21 days old and student accounts.

Is there a way to check both the user folder and system preferences simultaneously, or individually works as well, for LDAP users?

Sorry for a long winded response, I was just really intrigued by all of this and have a lot of questions,

gmillercmsd12
New Contributor

@Sean_Ginn @cbrewer
I am dealing with the same issue. Using AD mobile accounts with a forced local home. I just went through my jump start and this was the one issue no one could help me resolve. All of our students are in the "student" group. If I just replace "bls students" with my "student" group will all work fine or is the local home going to cause me issue beyond my understanding at this early stage?

Thanks, this community is an amazing resource.