\documentclass{ictlab} % Copyright (c) 2003 by Nick Urbanik . % This material may be distributed only subject to the terms and % conditions set forth in the Open Publication License, v1.0 or later % (the latest version is presently available at % http://www.opencontent.org/openpub/). \RCS $Revision: 1.4 $ \usepackage{key,verbatim,alltt} \ifx\pdftexversion\undefined \else \usepackage[pdfpagemode=None,pdfauthor={Nick Urbanik}]{hyperref} \fi \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{http://cpan.org/modules/by-module/Win32/JHELBERG/lanman.1.0.10.0.zip}. \subsection*{Procedure:} \subsubsection*{Installing the two Perl modules} \paragraph{Setting the PPM proxy (ONLY ON THE CAMPUS, not at home):} 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.0.10.0.zip} and unzip it into a subdirectory of \texttt{D:\bs temp}, say, \texttt{D:\bs temp\bs lanman}. \item Open a command prompt and change to \texttt{D:\bs temp\bs lanman} (I assume that you have installed \textsf{ActiveState ActivePerl} from \url{http://ictlab.tyict.vtc.edu.hk/ftp/perl/ActivePerl-5.8.3.809-MSWin32-x86.msi}). \item Type (at the \texttt{C:\bs{}temp\bs{}lanman>} prompt): \begin{alltt} D:\bs{}temp\bs{}lanman>\textbf{ppm} PPM - Programmer's Package Manager version 3.1. Copyright (c) 2001 ActiveState SRL. All Rights Reserved. Entering interactive shell. Using Term::ReadLine::Stub as readline library. Type 'help' to get started. \end{alltt} \item Next, add the current directory ``\texttt{\textbf{.}}'' as a \acro{PPM} repository, called here, ``\texttt{CURRENTDIR}'': \begin{alltt} ppm> \textbf{rep add CURRENTDIR .} \end{alltt} Note; you can type ``help rep'' at the \texttt{ppm>} prompt. \item See if \acro{PPM} can find lanman: \begin{alltt} ppm> \textbf{search lanman} Searching in Active Repositories 1. Win32-Lanman [1.0.10] Contains the most important calls from the MS-Lanma~ \end{alltt} \item Good, so let's install it: \begin{alltt} ppm> \textbf{install Win32-Lanman} ==================== Install 'Win32-Lanman' version 1.0.10 in ActivePerl 5.8.3.809. ==================== Downloaded 229487 bytes. Extracting 21/21: blib\bs{}html\bs{}site\bs{}lib\bs{}win32\bs{}lanman.html 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 Files found in blib\bs{}arch: installing files in blib\bs{}lib into architecture depende nt library tree Installing D:\bs{}Perl\bs{}site\bs{}lib\bs{}win32\bs{}lanman.pm Successfully installed Win32-Lanman version 1.0.10 in ActivePerl 5.8.3.809. ppm> \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. The first step is to add David Roth's repository to \texttt{ppm}; the second is to download and install \texttt{Win32::Perms}. \begin{enumerate} \item At the \texttt{ppm>} prompt in \texttt{CMD.EXE}, type at the \texttt{ppm>} prompt: \begin{alltt} ppm> \textbf{rep add ROTH http://www.roth.net/perl/packages/} Repositories: [1] ActiveState PPM2 Repository [2] ActiveState Package Repository [3] ROTH ppm> \textbf{search perms} Searching in Active Repositories 1. Win32-Perms [0.2002.~ The Win32::Perms extension for Win32 X86. Manages ~ ppm> \textbf{install Win32-Perms} ==================== Install 'Win32-Perms' version 0.2002.06.05 in ActivePerl 5.8.3.809. ==================== Downloaded 97493 bytes. Extracting 12/12: blib/lib/Win32/PERMS.PM Installing D:\bs{}Perl\bs{}site\bs{}lib\bs{}auto\bs{}Win32\bs{}Perms\bs{}Perms.DLL Files found in blib\bs{}arch: installing files in blib\bs{}lib into architecture dependent library tree 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{}noinher.pl 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 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{}README Successfully installed Win32-Perms version 0.2002.06.05 in ActivePerl 5.8.3.809. ppm> \textbf{quit} \end{alltt} \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 element in an array element 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-2003.txt} to generate accounts for all those thousands of students. \end{document}