Scripting User Account Removal

Ok, so I've been looking all over the place for how exactly to do this and I've become so bombarded with information I finally decided I'll pose the question here. I'm not a programmer or anything that hardcore, but if I see things already implemented and working examples I can easily learn and modify them to suit my needs. That being said I have been trying to find the step by step or close to for creating a couple login/logout hooks. And now I'm also reading I should create a launchd job instead and am just beginning to look into that option since it's more secure.

I'm in a mixed 10.6 / 10.7 environment with all moving to 10.7 hopefully soon here in a university environment and manage all the workstations we have in our building. Users log in with their AD accounts and user folders are created locally and pulled from the default new user template that I've modified to suit our environment.

The two (or maybe one?) scripts I need are to do a couple things then. First, I'd like to erase each user account on logout so the user folders don't pile up in the User directory. And then second, I'm thinking I'll also need a login script to put my Admin account folder back each time from a template or something as it too will get erased from the logout script.

Thanks for any help in advance.

step one: get the currently logged in user name.
If it were a loginhook, $1 would work. I've found that problematic for logouthooks, so I use $USER.

step two: verify they aren't admin, or aren't you.
You could test the result of
dscl . -read /Groups/admin GroupMembership | grep "$USER"
If $? (the exit code) equals 0 then leave the logouthook script with "exit 1"
otherwise...

step three: remove the user account
dscl . -delete /Users/$USER

step four: remove the user's home folder
rm -R /Users/$USER

# If user is an admin, exit script
dscl . -read /Groups/admin GroupMembership | grep -q "$USER"
if [ "$? -eq 0 ]; then
# the next line could be substituted for the previous 2 lines
#if [ $USER = "adminuser1" ] || [ $USER = "adminuser2" ] || [ $USER = "adminuser3" ]; then
echo "LOGOUT: admin folders will not be deleted."
exit 1
fi

# If home directory exists, delete
if [ -d "/Users/$USER" ]; then
echo "LOGOUT: user account cleanup."
rm -R /Users/"$USER"
dscl . -delete /Users/"$USER"
fi
exit 0

That should do it. I use a similar script and it works fine except for forced reboot scenarios, but that's what lab admins, and periodic reimaging is for. :wink:

This is fairly rudimentary scripting. Feel free to use awk, case statements, and for loops to your hearts content. :slight_smile:

1 Like

Hey thanks for the message man! I'm a little unclear what all to copy/paste into my text file though. (sorry) As I said, new to all this stuff and not clear on a lot of it yet. Any chance you can create a super dummies version of the email you sent? I'm reading one article here, logout hooks for dummies - Apple Community, for example, and it's confusing me where to put the file and what exactly I'm writing. I'm using 10.7 on all the machines I want to put this on.

from "# If..." to "exit 0"

Your script should actually start with:
#!/bin/bash
# and insert any comments here, like what this script does

Then you want to name it such that you won't accidentally try to use it as a loginhook. My recommendation is "LogoutHook". :slight_smile:

You can put the script practically anywhere, but try to think of a place that will be common for all such scripts going forward. We created a folder in the main Library folder for all custom scripting. That is where all of our admin level support staff can find them on any previous or future image builds.

You probably don't want read privileges for other, aka "world readable". After creating your script, from the command line, enter this and hit return:
chmod 770 /path/to/LogoutHook

You probably want to make root or "system" the owner, and maybe assign the admin group full access:
sudo chown root:admin /path/to/LogoutHook

I'm sure someone will correct me if I'm wrong on any of this, but it is working this way for my scripts.

1 Like

greetings

here is an applescript i wrote a while ago. it uses a filemaker database to grab the users name and information. I had one written in bash, but I can't find it,.

tell application "FileMaker Pro"
	set RecordName to cell "RecordName" of current record
end tell
display dialog " WARNING !!!!!!!

THIS ACTION WILL
DELETE THE ACCOUNT ......." & RecordName & "......


Are you sure you want to proceed ? " with icon 0 with title " Time to Delete " buttons {"Exit", "Continue"} default button "Continue"
if button returned of the result is "Continue" then
	try
		do shell script "dscl -u diradmin -P PASSWORD /LDAPv3/my.ldap1.com -delete /Users/" & RecordName & ""
		beep 3
		display dialog " username " & RecordName & " Was Deleted from ODM" with icon 1 with title " Laters " buttons {"Okay F00"} default button 1
	on error errMsg number errorNumber
		display dialog errMsg
	end try
else
	display dialog "SEE YA f00!!!"
	beep 1
end if

Thanks guys. Just getting to my new images now so I'll keep you posted as to whether or not I hose the machines. :wink:

Maybe I'm creating the script wrong or something, for it's not cooperating. The part you said to copy/paste, should that just be put into a new TextEdit doc and saved out as a plain text file?

And I can copy directly what you entered and not need to modify anything?

Like I said, noob to the scripting here. :frowning:

copy and paste the script in AppleScript

or

use a text editor and create a Shell Script

#!/bin/bash
clear
bold=`tput smso`
offbold=`tput rmso`
echo "Section 1";
aName=joeuser

#check for user
CKU1=`dscl /LDAPv3/ldap1.mydomain.com/ -read Users/$aName RecordName UniqueID`
echo -e "--------------------------------------------------- ";
echo $CKU1
echo -e "--------------------------------------------------- ";
if [[ -z $CKU1 ]] 
then 
echo blamo the account is NOT active on ldap1
else
echo -e "\033[33;32m OK \033[31;0m,.. the User is\033[32;32m ${bold} ACTIVE ${offbold}on LDAP1 \033[32;0m"
fi

So I've tried to copy/paste both suggested codes from [MA]Flying_Meat and the most recent one from doctorfoo1 into AppleScript, but when I go to save out and compile both error out with a syntax error. :frowning:

[MA]Flying_Meat: yours errors on line 2 right at the . after dscl

doctorfoo1: yours errors on the ` just before tput

I'm beginning to remember why I got so frustrated trying to learn java and giving up.

okay ,..
your probably getting a dscl error. have you tried the dscl read directly in the terminal ?

dscl /LDAPv3/ldap1.mydomain.com/ -read Users/$aName RecordName UniqueID

if that fails, you need to connect your MAC ( the one doing the work ) to the Directory.
depending on your OS version, open Directory utility and add your LDAP server to the list of services and search policies.

osx 10.6.8 you'll find the utility in /System/Library/CoreServices/Directory Utility

"Users log in with their AD accounts and user folders are created locally and pulled from the default new user template that I've modified to suit our environment."

I presume the account credentials are cached locally. If not, then all you have to do is remove the home folder.

If the account credentials are cached locally, then the user can log in without a network connection. You will need to delete the locally cached user account data using the dscl command.

You can try:
dscl localhost -read...
or:
dscl localonly -read...

But dscl . -read... is valid according to the man page for dscl:
man dscl

ldap may be a dead end, as you are binding machines to AD, which doesn't generally require configuring the generic ldap directory service at all.

All of the machines are already bound to the Active Directory from the Directory Utility. At this point all units are on 10.6.8, we'll soon be moving to 10.7 however I'd like to get this figured out prior to the upgrade. User account data is not cached to the machine so if the network cable is unplugged and I delete the Home folder for a specific user, they are not able to log in again until that network connection is reestablished so it can find the AD when someone tries to log in again.

In the past I've simply gone around to each machine about once a week and removed the entire contents of the /Users folder in the Terminal using sudo and rm -R, except for the local admin and the Shared folder. So I'm just trying to script that manual process.

Does that help clarify?

When I paste the script you wrote into AppleScript Editor [MA]Flying_Meat, I then hit Compile, and then it errors. Should I not be trying to compile this? And when I just go to save it as LogoutHook.sh and not compile, it says I have to save it as a .scpt. Should I be trying to save it as just text? (If it can ever get to that point)

It is a shell script, which is basically just a strict text document with execute privileges.

My favorite text editor is TextWrangler. Its free and saves documents as text by default. TextEdit can too, but you need to set the preferences to save as text, and be aware that the Lion version of TextEdit is known to save version info, which can muck with what otherwise would be strictly text. Get TextWrangler from the BareBones web site, not the App Store, as the App Store version is a little crippled due to App Store policy. It's a great product.

If you would like to become more familiar with Unix text editing, but require a somewhat smaller learning curve than VI, then try using nano.
There is a help menu at the bottom of the nano "window"

sudo nano LogoutHook.sh
You can copy and paste into the nano window at the current cursor position, but with most things Unix, there isn't much in the way of mouse support within the application. :stuck_out_tongue: :wink:

Ok, I've got the script in place, and the write command set, but I think the code had an error in it?

# If user is an admin, exit script 
dscl . -read /Groups/admin GroupMembership | grep -q "$USER"
if [ "$? -eq 0 ]; then
# the next line could be substituted for the previous 2 lines
#if [ $USER = "adminuser1" ] || [ $USER = "adminuser2" ] || [ $USER = "adminuser3" ]; then 
echo "LOGOUT: admin folders will not be deleted."
exit 1
fi 

# If home directory exists, delete 
if [ -d "/Users/$USER" ]; then 
echo "LOGOUT: user account cleanup."
rm -R /Users/"$USER" 
dscl . -delete /Users/"$USER"
fi 
exit 0

I put a closing " after the first if [ "$? -eq 0 and now it runs properly. Hehehe, however, it didn't see my admin user as an admin and wiped that out. When I use the substituted line that specifies the user it then works properly.

Totally awesome. Thank you so so much for the help.

Now, is there a way to put a time stamp on that? So say I want to delete the user folder after a period of 24hrs. So if a user doesn't come on that machine for 24 hrs then it gets wiped, but if they happen to come and use that same machine within the specified time period, it skips it.

Ah! Sorry. The quoting thing gets me periodically. When to quote/not to quote, which quote method... You can probably remove the quotes from within that particular if statement and it would probably still work. :stuck_out_tongue:

A time stamp operation would necessarily require a rework of the logic behind the logout script. Instead of using the current user ($USER) you would have to loop through every user profile EXCEPT the current user.

It can be done, but...

Here is an example of a loop for that sort of thing:

for i in `ls /Users`
do
	if [ $i = ".localized" ] || [ $i = "Shared" ] || [ $i = $USER ]; then
		# go to next folder in the loop
		continue
	else
		# do something with the folder, for instance:
		# rm -R /Users/"$i"
		echo "$i removed"
	fi
done

You would insert your timestamp check and deletion routine after the "else", presumably by nesting an if statement there. Failing the timestamp check would require continuing the loop, a la "continue" as seen above.

The time stamp check value would be the current day and time minus 1 day.
This should help:
http://www.unix.com/answers-frequently-asked-questions/13785-yesterdays-date-date-arithmetic.html

Where you get the time stamp from on the user's home folder? My own home folder does show my login time, as do a couple of preference files in my home folder's Library/Preferences. You will need to kajigger the date info to work correctly in a comparison. You'll also want to verify the specific time stamp file as the valid choice across a few different logins, both admin and non admin, just to be sure.

Let us know if you achieve nirvana. :slight_smile:
(I vaguely recall seeing something along these lines in some long ago Enterprise or Edu list...)

---------- Post updated at 04:59 PM ---------- Previous update was at 01:56 PM ----------

Or!

find /Users -maxdepth 1 \! -mtime -1d

Will return all folders that have not been modified in the last 24 hours (again, testing is important.)

find /Users \( \! -name Users -and \! -name .localized -and \! -name Shared -and \! -name $USER \) -maxdepth 1 \! -mtime -1d

Apparently (within the limits of maximum command line characters per line) you can keep adding "-and \! -name username" entries till you're satisfied.

So:

find /Users \( \! -name Users -and \! -name .localized -and \! -name Shared -and \! -name $USER \) -maxdepth 1 \! -mtime -1d -exec rm -R {} \;

Note the first exclusion "Users". Very important!
The find command output without exclusions looks like this:
$ find /Users -maxdepth 1 \! -mtime -1d
/Users
/Users/.localized
/Users/Shared

You probably do not want to recursively delete the /Users directory. :eek:

---------- Post updated at 05:13 PM ---------- Previous update was at 04:59 PM ----------

P.S.
I found this nugget at
CLI Fun: Delete files older than x days

1 Like

Hehe, the nirvana comment had me loling.

Ok, so the string:

find /Users \( \! -name Users -and \! -name .localized -and \! -name Shared -and \! -name $USER \) -maxdepth 1 \! -mtime -1d -exec rm -R {} \;

I'm trying to understand, actually if you can recommend a great book that describes all this stuff I'd love to know of it. Like I said, I can usually pick apart and fix stuff that already exists, but making my own is beyond me.

So if I wanted to implement that line to find and remove anything after 1 day would it look like this?

#!/bin/bash
# Logout script to remove nonadmin accounts from Users folder
# If user is an admin, exit script 
if [ $USER = "JoeUser" ]; then
echo "LOGOUT: admin folders will not be deleted."
exit 1
fi 

# If home directory exists, delete 
find /Users \( \! -name JoeUser -and \! -name .localized -and \! -name Shared -and \! -name $USER \) -maxdepth 1 \! -mtime -1d -exec rm -R {} \;
dscl . -delete /Users/"$USER"
fi 
exit 0

So then that would make it skip the user "JoeUser", and the "Shared" folder?

Your find line:
Don't forget \! -name Users

Otherwise, yes It would not delete JoeUser, .localized, Shared, nor $USER.

You definitely want to exclude Users, as that would get deleted if it's time stamp is older than a day. Bad, since every home folder you wanted to keep would get deleted too.

You could remove the "exit 1" line from the script so that you still get notification that your admin user account/s will not be deleted. Your first if statement would then just for the notification, proceeding on to the find and delete operation. It should look like this:

# If home directory exists, delete 
find /Users \( \! -name Users -and \! -name JoeUser -and \! -name .localized -and \! -name Shared -and \! -name $USER \) -maxdepth 1 \! -mtime -1d -exec rm -R {} \;
dscl . -delete /Users/"$USER"
exit 0

---------- Post updated at 05:55 PM ---------- Previous update was at 05:32 PM ----------

As far as creating your own goes:
Advanced Bash-Scripting Guide

Verrrry handy.

Other than that, there's a "Learn Shell Scripting in 24 Hours" which even if it takes 24 days still isn't bad. Also web search, maybe even a quick and dirty Unix class at a local Community College?

It should be said that there is usually more than one way to accomplish the same task. Some might make use almost exclusively of awk, while others might never get much outside of python. Still others might scoff at not using ruby.

I've found that these Unix forums provide real examples of tasks one might need to perform, particularly Shell Programming and Scripting, Unix for Dummies... and Unix for Advanced. I have spent a few hours since finding these forums trying code out to see what it does. If the commands are not exclusive to a particular version of Unix, then it will be informative.
I find shell scripting (bash and/or sh) to be adequate, if admittedly sometimes clunky, for my needs. Learning something else is almost always limited to finding I cannot complete the task in the shell by itself. My most recent tenuous forays into the unknown have been simple awk line parsing with it's marvelous print function.
Pretty darned rare, but then I'm a simple kinda guy.

1 Like

That find on the "remove after x days" was a sweet score! :cool:

I actually set it to 3 days to give the inevitable student that forgot to save to a flash drive the opportunity to save face. Though more often than not it's the Grad students that haven't learned this lesson yet and they lose months of work.

That did it. I've got it running on all our units without flaw. So now what?

Thanks so much for all the help [MA]Flying_Meat. And for the tips on what to read. I actually started teaching myself some Objective-C and even the little I've picked up so far in that helped me understand more some of the code you'd written.

I think the problem is finding and using on a regular basis practical uses for creating scripts. I'm not in an environment that requires much of it and being the only one doing it it makes it more of a challenge.

Any idea how scripting for Windows differs?