\documentclass{ictlab} \RCS $Revision: 1.4 $ \usepackage{key,verbatim,alltt} \usepackage[pdfpagemode=None,pdfauthor={Nick Urbanik}]{hyperref} \newcommand*{\labTitle}{Creating User Accounts in Windows 2000 with Perl} \begin{document} \subsection*{Background:} We will use code that is for the most part written by David N. Blank-Edelman in his excellent book, \emph{Perl for System Administration}, on pages 84 and 85. I have modified it to make it more general, in that we can use it to easily create accounts from the data provided by the \VTC \acro{GRS} (Grade Reporting System)\@. There are two additional Perl modules required for this, however. We need to download and install them. I have downloaded one, \texttt{Win32::Lanman}\@. You will install the other, \texttt{Win32::Perms} yourself using the \acro{PPM} (Perl Package Manager) progam. You can read about \acro{PPM} from the \textsf{ActiveState ActivePerl} documentation. I downloaded \texttt{Win32::Lanman} from \url{ftp://ftp.roth.net/pub/ntperl/Others/Lanman/}. \subsection*{Procedure:} \subsubsection*{Installing the two Perl modules} \paragraph{Setting the PPM proxy:} Set the environment variable \texttt{HTTP\_proxy} to the value \url{http://proxy.vtc.edu.hk:8080}, or what value actually works. To do this: \begin{enumerate} \item right click on ``\texttt{My Computer}'' (No, yours, not mine). \item Click on \textsf{Properties} \item Click on \textsf{Advanced} (yes, we are advanced in so many ways!) \item Click on \textsf{Environment Variables} \item Click on the \key{\textsf{New\ldots}} button under \textsf{System variables} \item in \textsf{Variable \underline{N}ame}, enter \texttt{HTTP\_proxy} \item in \textsf{Variable \underline{V}alue}. enter the proxy that works for you: \begin{itemize} \item \texttt{http://hqproxy.vtc.edu.hk:8080} \end{itemize} \item Click on all appropriate \key{\textsf{OK}} buttons. \end{enumerate} \paragraph{Installing \texttt{Win32::Lanman}} \begin{enumerate} \item Download \url{http://ictlab.tyict.vtc.edu.hk/ftp/perl/lanman_1_09_1.zip} and unzip it into a subdirectory of \texttt{D:\bs temp}. \item Open a command prompt and change to \texttt{D:\bs temp} (I assume that you have installed \textsf{ActiveState ActivePerl} from \url{http://ictlab.tyict.vtc.edu.hk/ftp/perl/ActivePerl-5.6.1.635-MSWin32-x86.msi}). Any 5.6.x version of Perl will do fine, but the \texttt{Lanman} module does not work with 5.8.x. \item Type (at the \texttt{C:\bs{}temp>} prompt): \begin{alltt} D:\bs{}temp\bs{}lanman>\textbf{ppm install win32-lanman --location=.} Installing package 'win32-lanman'... Installing D:\bs{}Perl\bs{}site\bs{}lib\bs{}auto\bs{}win32\bs{}lanman\bs{}lanman.dll Installing D:\bs{}Perl\bs{}site\bs{}lib\bs{}auto\bs{}win32\bs{}lanman\bs{}lanman.exp Installing D:\bs{}Perl\bs{}site\bs{}lib\bs{}auto\bs{}win32\bs{}lanman\bs{}lanman.lib Installing D:\bs{}Perl\bs{}html\bs{}site\bs{}lib\bs{}win32\bs{}lanman.html Installing D:\bs{}Perl\bs{}site\bs{}lib\bs{}win32\bs{}lanman.pm \end{alltt} \item Note that the documentation for the \texttt{Win32::Lanman} module is now installed with the documentation for \textsf{ActiveState ActivePerl}\@. \end{enumerate} \paragraph{Installing \texttt{Win32::Perms}} Here we are going to use the \texttt{ppm} program to download and install the package from Internet\@. There is one additional complication here: we need to install the package from David Roth's web site instead of from the default, ActiveState's web site. \begin{enumerate} \item In the \texttt{Command Prompt}, type at the \texttt{D:\bs{}temp>} prompt: \begin{alltt} D:\bs{}temp> \textbf{ppm install http://www.roth.net/perl/packages/win32-perms.ppd} \end{alltt} You will see something like this: \begin{alltt} D:\bs{}temp>\textbf{ppm install http://www.roth.net/perl/packages/win32-perms.ppd} Installing package 'http://www.roth.net/perl/packages/win32-perms.ppd'... Downloading http://www.roth.net/perl/packages/x86/Win32/Perms_5006.tar.gz ... Installing D:\bs{}Perl\bs{}site\bs{}lib\bs{}auto\bs{}Win32\bs{}Perms\bs{}Perms.DLL Installing D:\bs{}Perl\bs{}site\bs{}lib\bs{}Win32\bs{}Perms.PM Installing D:\bs{}Perl\bs{}site\bs{}lib\bs{}Win32\bs{}Perms\bs{}COPY.PL Installing D:\bs{}Perl\bs{}site\bs{}lib\bs{}Win32\bs{}Perms\bs{}History.txt Installing D:\bs{}Perl\bs{}site\bs{}lib\bs{}Win32\bs{}Perms\bs{}noinher.pl Installing D:\bs{}Perl\bs{}site\bs{}lib\bs{}Win32\bs{}Perms\bs{}README Installing D:\bs{}Perl\bs{}site\bs{}lib\bs{}Win32\bs{}Perms\bs{}RECURSE.PL Installing D:\bs{}Perl\bs{}site\bs{}lib\bs{}Win32\bs{}Perms\bs{}ShowPerm.pl Installing D:\bs{}Perl\bs{}site\bs{}lib\bs{}Win32\bs{}Perms\bs{}TEST.PL Installing D:\bs{}Perl\bs{}site\bs{}lib\bs{}Win32\bs{}Perms\bs{}test2.pl Installing D:\bs{}Perl\bs{}site\bs{}lib\bs{}Win32\bs{}Perms\bs{}Win32-Perms.ppd \end{alltt} %% C:\temp>ppm install http://www.roth.net/perl/packages/win32-perms.ppd %% Installing package 'http://www.roth.net/perl/packages/win32-perms.ppd'... %% Bytes transferred: 96068 %% Installing C:\Perl\site\lib\auto\Win32\Perms\Perms.DLL %% Installing C:\Perl\site\lib\Win32\PERMS.PM %% Installing C:\Perl\site\lib\Win32\Perms\COPY.PL %% Installing C:\Perl\site\lib\Win32\Perms\Dacl.pl %% Installing C:\Perl\site\lib\Win32\Perms\NOINHER.PL %% Installing C:\Perl\site\lib\Win32\Perms\README %% Installing C:\Perl\site\lib\Win32\Perms\RECURSE.PL %% Installing C:\Perl\site\lib\Win32\Perms\ShowPerm.pl %% Installing C:\Perl\site\lib\Win32\Perms\TEST.PL %% Installing C:\Perl\site\lib\Win32\Perms\TEST2.PL %% Writing C:\Perl\site\lib\auto\Win32\Perms\.packlist \item Note that the documentation for the \texttt{Win32::Perms} module is the file \texttt{D:\bs Perl\bs site\bs lib\bs Win32\bs Perms\bs README} \end{enumerate} \subsection*{The code to create user accounts} \label{sec:user-acct-create} This code (from David N. Blank-Edleman) implements adds a user: \begin{verbatim} use strict; #* #* basic local user account creation routine for NT/2000 #* use Win32::Lanman; # for account creation use Win32::Perms; # to set the permissions on the home directory # our $homeNTdirs = "\\\\servername\\home"; # home directory root dir our $homeNTdirs = "D:\\home"; # home directory root dir # returns false if successful, true and an error message if there is a # problem. Rather un-Perl like, more like C. sub CreateNTAccount { my ( $account, $password, $fullname, $group ) = @_; # create this account on the local machine # (i.e. empty first parameter) my $result = Win32::Lanman::NetUserAdd( "", { name => $account, password => $password, home_dir => "$homeNTdirs\\$account", full_name => $fullname, # expires in 60 days from now: acct_expires => 3600 * 24 * 60 + time() } ); return Win32::Lanman::GetLastError() unless $result; my @info; # add to appropriate LOCAL group (first get the SID of the account) die "SID lookup error: ".Win32::Lanman::GetLastError()."\n" unless Win32::Lanman::LsaLookupNames( "", [ $account ], \@info ); $result = Win32::Lanman::NetLocalGroupAddMember( "", $group, ${$info[0]}{sid} ); return Win32::Lanman::GetLastError() unless $result; # create home directory mkdir $homeNTdirs, 0777 unless -d $homeNTdirs; mkdir "$homeNTdirs\\$account", 0777 or return "Unable to make homedir:$!"; # now set the ACL and owner of the directory my $acl = new Win32::Perms( "$homeNTdirs\\$account" ); $acl->Owner( $account ); # we give the user full control of the directory and all of the # files that will be created within it (hence the two separate calls) $acl->Allow( $account, FULL, DIRECTORY|CONTAINER_INHERIT_ACE ); $acl->Allow( $account, FULL, FILE|OBJECT_INHERIT_ACE|INHERIT_ONLY_ACE); $result = $acl->Set(); $acl->Close(); return $result ? "" : $result; } \end{verbatim}%$ \ldots and this code (also from David) implements delete a user: \begin{verbatim} #* #* basic account deletion routine for NT/2000 #* use Win32::Lanman; # for account deletion use File::Path; # for recursive directory deletion sub DeleteNTAccount { my $account = shift; my @info; # remove user from LOCAL groups only. If we wanted to also # remove from global groups we could remove the word "Local" from # the two Win32::Lanman::NetUser* calls *e.g. NetUserGetGroups) die "SID lookup error: ".Win32::Lanman::GetLastError()."\n" unless ( Win32::Lanman::LsaLookupNames( "", [ $account ], \@info ) ); my @groups; Win32::Lanman::NetUserGetLocalGroups( "", $account, '', \@groups ); foreach my $group ( @groups ) { print "Removing user from local group ".$group->{name}."..."; print( Win32::Lanman::NetLocalGroupDelMember( "", $group->{name}, ${$info[0]}{sid} ) ? "succeeded\n" : "FAILED\n" ); } # delete this account on the local machine # (i.e. empty first parameter) my $result = Win32::Lanman::NetUserDel( "", $account ); return Win32::Lanman::GetLastError() if $result; # delete the home directory and its contents $result = rmtree( "$homeNTdirs\\$account", 0, 1 ); # rmtree returns the number of items deleted, # so if we deleted more than 0,it is likely that we succeeded return $result; } \end{verbatim}%$ And here is a bit of code to test it with: \begin{verbatim} my $result = CreateNTAccount( "test", "testpasswd", "The Test User", "Users" ); unless ( $result ) { print "good, it worked\n"; } else { print "Cannot create test account: $result"; } \end{verbatim} Okay, these subroutines use some features of the Perl programming language that we didn't cover in lectures. But we never aimed to cover every feature of Perl in just five weeks. For example, \texttt{\$\{\$info[0]\}\{sid\}} is a hash entry in an array that we refer to by a reference. And no, I won't explain that here; you can find that out for yourself, or ask me out of class hours. \subsection*{Now Create Those Thousands of Accounts on the Windows server} \label{sec:create-accounts} Use these two subroutines and the file \url{http://ictlab.tyict.vtc.edu.hk/snm/lab/regular-expressions/artificial-student-data.txt} to generate accounts for all those thousands of students. \end{document}