Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations TouchToneTommy on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Bulk User Generation

support utilities

Bulk User Generation

by  columb  Posted    (Edited  )
I have to create significant numbers of users across multiple systems on a regular basis. I soon got very bored of repeating the samce commands over and over again so....

The new users arrive as an e-mail giving
<user name> <full name>
<user name> <full name>
<user name> <full name>
<user name> <full name>
.......

This is cut and pasted into a unix file which is processed by a perl script mkusers.pl which looks like
Code:
#!/bin/perl -w
use strict;
 
 
my %users;
my $ucount = 0;
 
sub fisher_yates_shuffle # Shuffles arrays
  {
  my $deck = shift;  # $deck is a reference to an array
  my $i = @$deck;
  while ($i--) 
    {
    my $j = int rand ($i+1);
    @$deck[$i,$j] = @$deck[$j,$i];
    }
  }
 
sub mkpasswd # Genrates passwords 
  {
  my $pwdref = shift;
  my @lets = qw ( a b c d e f g h i j k l m n o p q r s t u v w x y z );
  fisher_yates_shuffle \@lets;
  my @other = qw ( ! $ % ^ & * \( \) { } [ ] );
  push @other, "#";
  my @pwdarr;
  $pwdarr[0] = uc ( $lets[0] );
  @pwdarr[1..5] = @lets[1..5];
  $pwdarr[6] = int ( rand ( 10 ) );
  $pwdarr[7] = $other[int rand ( $#other + 1 )];
  fisher_yates_shuffle \@pwdarr;
  $$pwdref = join "", @pwdarr;
  }
  
 
defined $ARGV[0] or die "No filename passed\n";
my $outfile;
my $errfile;
$ARGV[0] =~ /(.*)\.txt$/ and $outfile = $1, $errfile = "$1.op" or die "Input file must end with .txt\n";

#open the files 
open FH_IN, $ARGV[0] or die "Unable to open $ARGV[0] for reading\n";
open FH_OUT, "> $outfile" or die "Unable to open $outfile for writing\n";
open FH_ERR, "> $errfile" or die "Unable to open $errfile for writing\n";

#read in the list of users int a hash
while (<FH_IN>)
  {
  chomp;
  /^\s*$/ and next;
  s/^\s+//;
  s/\t+/:/;
  my ( $user, $gecos ) = split /:/;
  $users{lc ($user)} = $gecos;
  $ucount++;
  }
close FH_IN;
 
print FH_ERR "There are $ucount new users\n";
 
#get /etc/passwd dets from host1 and host2
my @pwd = ( `rsh host1 "cat /etc/passwd"`, `rsh host2 "cat /etc/passwd`  );

#Find highest used uuid between 15000 and 16000
#This is the range I use - modify as required 
my $maxuid = 0;
foreach ( @pwd )
  {
  my ( $uname, $passwd, $uid ) = split /:/;
  ( $uid >= 15000 ) && ( $uid < 16000 ) && ( $uid > $maxuid ) and $maxuid = $uid;
  }
 
print FH_OUT "#!/bin/ksh\n";

USERLOOP: foreach my $newuser ( keys %users )
  {
  foreach ( @pwd ) #Check the passwd files
   {
   /^${newuser}:/ and (print FH_ERR "$users{$newuser} is already registered\n"), next USERLOOP;
   }
  
  $maxuid++; #Use the next UID
  #Set up the mkuser lines
  #You'll probably have to edit this bit
  #to suit your needs
  print FH_OUT "mkuser id=$maxuid \\
        pgrp=staffwar \\
        home=/usr/staffware \\
        gecos=\"$users{$newuser}\" \\
        login=false \\
        rlogin=false \\
        $newuser\n";
  my $pwdstring;
  mkpasswd \$pwdstring;
  print FH_OUT "echo \'password for $newuser is $pwdstring\'\n";
  print FH_ERR "$newuser - $pwdstring\n";
  print FH_OUT "/usr/local/bin/setpass -u $newuser -p \'$pwdstring\'\npwdadm -c $newuser\n";
  }
close FH_OUT;
close FH_ERR;

This generates a ksh script which looks like
Code:
#!/bin/ksh
 
export BOXNAME=$(uname -n | cut -c1,3,4)
 
mkuser id=15638 \
        pgrp=staffwar \
        home=/usr/staffware \
        gecos="John Smith" \
        login=false \
        rlogin=false \
        jsmith
echo 'password for jsmith is w$4jyXlz'
/usr/local/bin/setpass -u jsmith -p 'w$4jyXlz'
pwdadm -c jsmith
mkuser id=15639 \
        pgrp=staffwar \
        home=/usr/staffware \
        gecos="Sue Brown" \
        login=false \
        rlogin=false \
        sbrown
echo 'password for sbrown is lhc#p2Aq'
/usr/local/bin/setpass -u sbrown -p 'lhc#p2Aq'
pwdadm -c sbrown
The final key to the process is /usr/local/bin/setpass. This came from code posted on this site by p5wizard (to whom many thanks) I know it works when compiled under AIX5.1 using either the AIX C compiler or gcc. The code looks like
Code:
/*******************************************************
* setpass                                              *
* sets AIX passwords in batch mode                     *
* options                                              *
*  -u <username>  Username of password to set          *
*  -t <timestamp> last amended time - defaults to      *
*                 current time                         *
*  -p <password>  password in plain text               *
*  -e <encrypted password> encrypted password          *
*  The -e and -p optins are mutually exclusive         *
*******************************************************/
 
#include <stdio.h>
#include <usersec.h>
#include <userpw.h>
#include <pwd.h>
#include <crypt.h>
#include <errno.h>
#include <sys/time.h>
#include <sys/types.h>
 
#define nogood(c) ((! isupper(c))&&(!isdigit(c))&&(c!='.')&&(c!='/'))
 
char *myencrypt ( char *pword )
  {
  char salt[2];
  time_t T;
  int i;
 
  T = time ( &T );
  srandom ( ( time (&T) % 32786 ) + getpid() );
  for ( i = 0; i < 2; i++ )
    {
    do
      {
      salt[i] = (char)random() & 0x007F;
      }
      while ( nogood ( salt[i] ) );
    }
  return ( crypt ( pword, salt ) );
  }
 
void print_usage ( char *progname, char *message, int exit_value )
  {
  fprintf ( stderr, "%s\nUsage:- %s \n\t-u <username> \n\t-p <plain text password string> | -e <encrypted password string> \n\t-t <timestamp>\n", message, progname );
  exit (exit_value);
  }
 
extern int errno;
extern char *optarg;
 
main(int argc, char *argv[])
{
 
  struct passwd   pw;
  struct userpw  *upw;
  char          **msgp;
  time_t          T;
  int             c;
  char           *uname = NULL;
  char           *pword = NULL;
  char           *cpword = NULL;
  char           *tstamp = NULL;
 
  if ( getuid() != 0 )
    print_usage ( argv[0], "You must be root to use this", 1 );
 
  while ( ( c = getopt ( argc, argv, "u:p:t:e:" ) ) != EOF )
    {
    switch (c)
      {
      case 'u' :
        uname = optarg;
        break;
      case 'e' :
        pword = optarg;
        break;
      case 'p' :
        cpword = optarg;
        break;
      case 't' :
        tstamp = optarg;
        break;
      case '?' :
        print_usage ( argv[0], "Unknown option", 1 );
        break;
      }
    }
 
  if ( ! uname )
    print_usage ( argv[0], "No user name specified", 1 );
 
  if ( ( ! pword ) && ( ! tstamp ) && ( ! cpword ) )
   print_usage ( argv[0], "At least one of password or timestamp must be specified", 1 );
 
  if ( pword && cpword )
    print_usage ( argv[0], "Either clear password OR encrypted password may be used", 1 );
  
  if ((setuserdb (S_WRITE)) != 0)
    print_usage ( argv[0], "Unable to open /etc/passwd for writing", 1 );
  if ((setpwdb (S_WRITE)) != 0)
    print_usage ( argv[0], "Unable to open /etc/security/passwd for writing", 1 );
 
  if ((putuserattr ( uname, S_PWD, "!", SEC_CHAR)) != 0)
    {
    if (errno == ENOENT)
      print_usage ( argv[0], "No entry for this user", 1 );
    else
      print_usage ( argv[0], "Unknown error editing /etc/passwd", 1 );
    }
 
  if ((putuserattr ( uname, S_ID, "0", SEC_COMMIT)) != 0)
    print_usage ( argv[0], "Unable to commit chages to /etc/passwd", 1 );
  
  if ( ( upw = getuserpw ( uname ) ) == NULL )
    print_usage ( argv[0], "Unable to get user details", 1 );
  if ( pword ) 
    upw->upw_passwd = pword;
  if ( cpword )
    upw->upw_passwd = myencrypt ( cpword );
  if ( tstamp )
    upw->upw_lastupdate = (time_t) atol ( tstamp );
  else
    upw->upw_lastupdate = (time_t ) time ( &T );
 
  if ((putuserpwhist ( upw, msgp)) != 0)
    print_usage ( argv[0], "Unable to update /etc/security/passwd", 1 );
  if ((enduserdb ()) != 0)
    print_usage ( argv[0], "Unable to close /etc/passwd", 1 );
  if ((endpwdb ()) != 0)
    print_usage ( argv[0], "Unable to close /etc/security/passwd", 1 );
  }
For those on AIX 5.3 ggautier has found chpasswd which apparently does the same thing.

It took a while to put the pieces together but now I can go from receiving an e-mail for 50 or more users to having those users installed on hosta and hostb within 30 mins or so.
Register to rate this FAQ  : BAD 1 2 3 4 5 6 7 8 9 10 GOOD
Please Note: 1 is Bad, 10 is Good :-)

Part and Inventory Search

Back
Top