remove /Users/nameOfUser but user is still listed in User Pane of System Preferences

tcandela
Valued Contributor II

I have a script that runs and deletes the home directories of users

userList=find /Users -type d -maxdepth 1 -mindepth 1 -not -name "." -not -name "admin" -not -name "testuser" -not -name "Shared" -not -name "Guest" -mtime +7 | awk -F'/' '{print $NF}'

for a in $userList ; do rm -r /Users/"$a"; #delete the home directory echo "$a has been deleted" let counter=counter+1
done

it deletes the home directories correctly, but when i check the System Preferences --> Users & Groups
all the deleted users names are still listed here. How do I also get those user names cleared ?

1 ACCEPTED SOLUTION

mm2270
Legendary Contributor III

You have to remove the user using dscl. Just deleting the home folder does not remove the account from the local directory services, and it will still be listed in Users & Groups as a result.

#!/bin/sh

userList=find /Users -type d -maxdepth 1 -mindepth 1 -not -name "." -not -name "admin" -not -name "testuser" -not -name "Shared" -not -name "Guest" -mtime +7 | awk -F'/' '{print $NF}'

for a in $userList ; do
      rm -r /Users/"$a"; #delete the home directory
      dscl . delete /Users/"$a" # Deleting the entry from dir services
      echo "$a has been deleted"
      let counter=$((counter+1))
done

I'm thinking that first line pulling the usernames could also be simplified a bit, but I don't want to get too off the topic with this post, so I'll leave it at that.

View solution in original post

23 REPLIES 23

mm2270
Legendary Contributor III

You have to remove the user using dscl. Just deleting the home folder does not remove the account from the local directory services, and it will still be listed in Users & Groups as a result.

#!/bin/sh

userList=find /Users -type d -maxdepth 1 -mindepth 1 -not -name "." -not -name "admin" -not -name "testuser" -not -name "Shared" -not -name "Guest" -mtime +7 | awk -F'/' '{print $NF}'

for a in $userList ; do
      rm -r /Users/"$a"; #delete the home directory
      dscl . delete /Users/"$a" # Deleting the entry from dir services
      echo "$a has been deleted"
      let counter=$((counter+1))
done

I'm thinking that first line pulling the usernames could also be simplified a bit, but I don't want to get too off the topic with this post, so I'll leave it at that.

tcandela
Valued Contributor II

@mm2270 I added the 'dscl' line you provided.

even though let counter=counter+1 was working for me, I changed it to let counter=$((counter+1))

after the 'do' loop runs, i added jamf recon

I'll test and get back.

ericbenfer
Contributor III

You are only deleting the home directory, not the user account.

If you want to use the jamf command, first take a look at the jamf command help page

/usr/local/bin/jamf help deleteAccount

You would likely use something like this:

/usr/local/bin/jamf deleteAccount -username $LocalUserName -deleteHomeDirectory

If you want to use a native OS X (10.10+) command first take a look at the sysadminctl command help page.

/usr/sbin/sysadminctl -help

You would likely use something like this:

/usr/sbin/sysadminctl -deleteUser $LocalUserName

tcandela
Valued Contributor II

which one of these 3 methods is the preferred method ?

jamf deleteAccount

sysadminctl - deleteUser

dscl . delete

mm2270
Legendary Contributor III

@tcandela That's a very good question. Here's what I can say about them.

jamf deleteAccount - this should work on all systems that are under management. As far as I know, there shouldn't be any problems with using this and the syntax is fairly simple to use. OTOH, since its a built in jamf binary command, there's always a possibility of it breaking at some point due to changes from Apple. But, I'd imaging JAMF would update their binary to accommodate any such changes.

sysadminctl -deleteUser - this (sysadminctl) is a relatively new binary that only came about as of 10.10 Yosemite. If you have a need to delete accounts on any Macs running older OSes, this isn't going to work. Its a good binary and is likely the one Apple engineers would recommend if asked, but again, keep in mind what your target OSes are with this one.

dscl . delete - an oldie but goodie. As of 10.11, this still should work just fine to remove an account from the Mac. As far as I've seen dscl has not been deprecated by Apple... yet. I say yet, because they do have a tendency to deprecate things as time goes on and they add new binaries, like sysadminctl.
I don't recommend using dscl for changing account records, like adding/removing accounts from the admin group. For that, either dseditgroup or sysadminctl are better tools, but for simply deleting an account, it should be OK to use dscl. To anyone out there, if I'm way off on that, feel free to let me know that I'm wrong.

bpavlov
Honored Contributor

sysadminctl is newer so it would work on 10.10 or higher. and i believe specifically on local accounts (not sure if it works on mobile accounts).

jamf deleteAccount is probably making use of dscl and other tools to remove the account.

i'd test and go with whatever works best for you. just know that if something changes with a newer OS you would have to rely on jamf to update that particular command which they may or may not do right away whereas apple would most likely definitely make sure that sysadminctl is up to date and working on the latest OS.

tcandela
Valued Contributor II

I added the dscl . delete /Users/"$a" line, just like above. I then add some test local accounts

test1, test2, test3

and then using Casper Remote I target the script to the test computer and 0 home directories get deleted.

all 3 test accounts do not get touched.

I then just added into the do loop jamf deleteAccount -userName $a -deleteHomeDirectory
and also nothing happened, all home folders and Users were not touched.

The comments inside the DO LOOP never even echoed

mm2270
Legendary Contributor III

Then something is wrong with the script in how its pulling the accounts to remove or passing them to the loop. It can't be that dscl and jamf deleteAccount are both broken.

try removing the double quotes around the $a variable. It generally shouldn't be needed since having spaces in a short user name is difficult (almost impossible actually, though not 100% impossible) so quoting the variable there shouldn't be needed.

mm2270
Legendary Contributor III

Ugh. I see the issue. Its the first line. No idea why I didn't see that, but it needs to be:

userList=$(find /Users -type d -maxdepth 1 -mindepth 1 -not -name "." -not -name "admin" -not -name "testuser" -not -name "Shared" -not -name "Guest" -mtime +7 | awk -F'/' '{print $NF}')

The way you have it its not creating a variable at all to loop over since its not contained in $() syntax or with backticks.

one other thing. I would change the rm -r to be rm -Rfd The "f" flag does a force removal and "d" tells it to also delete the directory its operating on, meaning the top level home folder. It may not be strictly necessary, but it should work a little better like that.

tcandela
Valued Contributor II

@mm2270 - I'll add the ( ) to the first line tomorrow and test it, with both the .dscl and jamf deleteAccount.

I put the jamf deleteAccount -userName $a -deleteHomeDirectory inside the Do Loop and just # comment out the rm -r and dscl lines.

in my original post it did not have the $() and went through the DO LOOP deleting the home directories, why now since adding the 'dscl . delete or jamf deleteAccount' does it need the $() ?

tcandela
Valued Contributor II

@mm2270 adding the $() made no difference

bpavlov
Honored Contributor

I could be mistaken here, but your userList variable is getting a list of all folders under /Users. But just because a folder exists there, does not mean that an account exists in the local directory service.

This command should give you a list of all users:

dscl . -list /Users

But you probably want to omit the OS specific accounts. Quick and dirty way:

dscl . -list /Users | grep -v "_" | grep -v "daemon" | grep -v "nobody" | grep -v "root"

And then once you have those user names you can get their home directories with the following command:

dscl . read /Users/USERNAME_GOES_HERE NFSHomeDirectory | cut -d " " -f 2

Keep in mind I'm not sure if NFSHomeDirectory is the correct key to look at. It seems like the right value though as it reports the proper info on a local and mobile account on my computer.

Anyways, I realize this may not exactly help with specific the issue, but it might be a more accurate way to determine which accounts are actually on the command (and if you want to determine the account's home directory assuming you don't want to use the jamf command or sysadminctl).

tcandela
Valued Contributor II

@mm2270 my original post did not show the back ticks, but did have them in the script. I've added the dscl . delete /Users/$a

it works, but only deletes users that have accounts created when logged in through an AD account.

If i specifically create a local account from the Users & Groups pane, those users do not get deleted. Why would these accounts I create locally (standard accounts) not get deleted?

Here is the script.

echo "Deleting home directory for the following users…"

userList=find /Users -type d -maxdepth 1 -mindepth 1 -not -name "." -not -name "admin" -not -name "user1" -not -name "Shared" -not -name "Guest" -mtime +7 | awk -F'/' '{print $NF}'

counter=0

for a in $userList ; do # jamf deleteAccount -userName $a -deleteHomeDirectory #rm -r /Users/"$a" ; #delete the home directory rm -Rfd /Users/"$a" ; #delete the home directory dscl . delete /Users/"$a" # Deleting the entry from dir services echo "$a has been deleted" let counter=counter+1
done

echo "Finished Deleting $counter home directories"

jamf recon

exit $?

bpavlov
Honored Contributor

when posting scripts make sure to select all your code and then use the button that has ">_" so that it preserves the code.

tcandela
Valued Contributor II

@bpavlov thanks, i was wondering how to do that. I then add my code between those ticks ?

#!/bin/sh

bpavlov
Honored Contributor

Correct. You can edit your previous posts.

tcandela
Valued Contributor II

I only want AD mobile accounts to be deleted/removed, but why does the script only remove those AD mobile accounts and not any test accounts that are created locally through the System Preferences --> Users & Groups pane ?

I don't see anything in the script that singles out AD mobile accounts

tcandela
Valued Contributor II

I figured out what was preventing my test accounts from also not being deleted.

it is that -mtime +7 that is toward the end of the script, once I removed this -mtime +7 (Primaries)

my 5 test accounts (home directories and entry in User & groups pane) were removed.

I read the -mtime _ definition under 'man find' but do not understand it.

jjones
Contributor II

Quick post of an edited version of mm2270's script from everyone's input. This is the best one that worked for me. Tested on 10.10 so far:

#!/bin/sh

#Add more | grep -v "" for users to keep at end of userList
userList=$(dscl . -list /Users | grep -v "_" | grep -v "daemon" | grep -v "nobody" | grep -v "root" | awk -F '/' '{print $NF}')

#Deleting Users and Directories
for a in $userList ; do
      /usr/local/bin/jamf deleteAccount -username "$a" -deleteHomeDirectory
      echo "$a has been deleted"
      let counter=$((counter+1))
done

jjones
Contributor II

Had a bit of fun with the script to integrate into Casper, still learning to script so critique away!

#!/bin/sh

#Last tested on 10.10 make sure you add any users to list below or it WILL DELETE user and files.

#If logic for variable username entries in Casper arugments
if [ -z "$4" ]; then 
    userList=$(dscl . -list /Users | grep -v "_" | grep -v "daemon" | grep -v "nobody" | grep -v "root" | awk -F '/' '{print $NF}')
    #Deleting User and userfiles
    for a in $userList ; do
        /usr/local/bin/jamf deleteAccount -username "$a" -deleteHomeDirectory
        echo "$a has been deleted"
        let counter=$((counter+1))
    done
    exit 0

else 
    if [ -z "$5" ]; then
        #Deletes Users including populated argument 4
        userList=$(dscl . -list /Users | grep -v "_" | grep -v "daemon" | grep -v "nobody" | grep -v "root" | grep -v "$4" | awk -F '/' '{print $NF}')
        #Deleting User and userfiles
        for a in $userList ; do
            /usr/local/bin/jamf deleteAccount -username "$a" -deleteHomeDirectory
            echo "$a has been deleted"
            let counter=$((counter+1))
        done
        exit 0

    else
        if [ -z "$6" ]; then
            #Deletes users including populated argument 5
            userList=$(dscl . -list /Users | grep -v "_" | grep -v "daemon" | grep -v "nobody" | grep -v "root" | grep -v "$4" | grep -v "$5" | awk -F '/' '{print $NF}')
            #Deleting User and userfiles
            for a in $userList ; do
                /usr/local/bin/jamf deleteAccount -username "$a" -deleteHomeDirectory
                echo "$a has been deleted"
                let counter=$((counter+1))
            done
            exit 0

        else
            #Deletes users including populated argument 6
            userList=$(dscl . -list /Users | grep -v "_" | grep -v "daemon" | grep -v "nobody" | grep -v "root" | grep -v "$4" | grep -v "$5" | grep -v "$6" | awk -F '/' '{print $NF}')
            #Deleting User and userfiles
            for a in $userList ; do
                /usr/local/bin/jamf deleteAccount -username "$a" -deleteHomeDirectory
                echo "$a has been deleted"
                let counter=$((counter+1))
            done
            exit 0
        fi
    fi
fi          

exit 0

sean
Valued Contributor

On the assumption that you don't have admin accounts that need preserving with home accounts located in /Users, you can just run through the user list and check for existence of file. This removes the need for any exception list that can change over time.

#!/bin/bash

dscl . list /Users | grep -v ^"_" | while read line
do
        if [ -e /Users/$line ]
        then
                echo "Deleting: $line"
                /usr/local/bin/jamf deleteAccount -username "$line" -deleteHomeDirectory
                echo "Account $line deleted: "$?
        fi
done

exit 0

If you do require to preserve any accounts then perhaps something akin to:

#!/bin/bash

dscl . list /Users | grep -v ^"_" | while read line
do
        if [ -e /Users/$line ]
        then
                case $line in

                        # Add as many OR as required for local admin with home accounts in /Users
                        admin_user1|admin_user2)
                                echo "admin user: $line"
                                ;;
                        *)
                                echo "Deleting: $line"
                                /usr/local/bin/jamf deleteAccount -username "$line" -deleteHomeDirectory
                                echo "Account $line deleted: "$?                                  
                                ;;
                esac
        fi
done

exit 0

Alternatively, you could use dscl to return the home directory and use that as your basis for deletion. Either way you aren't managing a potentially changeable exclusion list.

#!/bin/bash

home_dir="/Users/"

dscl . list /Users | grep -v ^"_" | while read line
do
        home_account=`dscl . read /Users/$line NFSHomeDirectory | awk '{print $NF}'`

        if [[ "$home_account" =~ ^"$home_dir" ]]
        then
                echo "Deleting: $line"
                /usr/local/bin/jamf deleteAccount -username "$line" -deleteHomeDirectory
                echo "Account $line deleted: "$?
        fi
done

exit 0

Again, add in the case statement if you have accounts to preserve.

The first will run faster, but I'd suggest the alternative is the better way to do it.

I would politely suggest that you remove the solution on @mm2270 post. This is a dangerous script to run. It will remove all of the hidden folders

find /Users -type d -maxdepth 1 -mindepth 1 -not -name "." -not -name "admin" -not -name "testuser" -not -name "Shared" -not -name "Guest" -mtime +7 | awk -F'/' '{print $NF}'
.DocumentRevisions-V100
.IABootFiles
.PKInstallSandboxManager
.Spotlight-V100
.Trashes

SGill
Contributor III

Did anyone find a simplified version of this script that just deletes the home folders for network accounts (AD) that don't have dscl entries to match? We switched from using mobilized AD accounts to using network AD accounts due to a problem that cropped up last year with how many accounts could be created without creating startup problems for the macOS. I'm not having too much luck with the one below from @mm2270 :

#!/bin/sh

userList=find /Users -type d -maxdepth 1 -mindepth 1 -not -name "." -not -name "admin" -not -name "testuser" -not -name "Shared" -not -name "Guest" -mtime +7 | awk -F'/' '{print $NF}'

for a in $userList ; do
      rm -r /Users/"$a"; #delete the home directory
      dscl . delete /Users/"$a" # Deleting the entry from dir services
      echo "$a has been deleted"
      let counter=$((counter+1))
done

Update - this older-than-7-day clear out ended up working for me for removing accounts that do not have a dscl entry, in case it helps anyone else:

#!/bin/sh

echo "Deleting home directory for the following users…"

userList=`find /Users -type d -maxdepth 1 -mindepth 1 -not -name "AdminAcct" -not -name "Shared" -mtime +7 | awk -F'/' '{print $NF}'`

for a in $userList ; do

rm -r /Users/"$a"; #delete the home directory

done

dmw3
Contributor III

Ok, dragging up an old discussion.

Trying to delete users that haven't logged in for n+ days but leaving some users exempt from deletion.

Have been getting different results depending on what command to delete is used, script below:

#!/bin/sh

userList=`find /Users -type d -maxdepth 1 -mindepth 1 -not -name "*.*" -mtime +7d | awk -F'/' '{print $NF}'`
LocalUsers=`dscl localhost -list /Local/Default/Users`
exempt=( itsadmin Shared Guest .localized )

for a in $userList ; do
  DeleteUser=1

#  for b in $LocalUsers ; do 

  for b in $exempt ; do

    if [[ "$a" = "$b" ]]; then
      # $a is a current user - don’t delete
      DeleteUser=0
    fi;

  done;

  if [[ "$DeleteUser" = "0" ]] || [[ "$a" = "Shared" ]] ; then
    echo "NO NOT DELETE $a"
  else
    echo "DELETE $a"
    rm -rf /Users/"$a";
#   sysadminctl -deleteUser /Users/"$a";
#   jamf -deleteAccount /Users/"$a";
  fi;

done

Results:
using:

rm -rf /Users/"$a";

removes the account locally but still lists the deleted accounts in the JSS Inventory.
using:

sysadminctl -deleteUser /Users/"$a";

returns an error:

Failed to authenticate with SystemAdministration framework

using:

jamf -deleteAccount /Users/"$a";

returns an error:

No user name was specified. Use the -username flag

Using the command that deletes the accounts, how can the same account be removed from the Inventory, or is another command a better choice?