programmatically clone /etc/skel for new users

/etc/skel is a folder that will be cloned for new users. Is there a possible way that we can define rules what to copy from /etc/skel folder?

For example, I'm looking for a way that if a user is created and belongs to group named A, clone /etc/skel folder except /etc/skel/not_for_a.txt. Possible?


Note that useradd command allows you to specify a custom SKEL directory using the -k option. You may create a /etc/skel-for-group-A directory without the not-for-a.txt, and then add new users with their default group as A, and specify their SKEL directory using the command:

useradd username -g groupA -k /etc/skel-for-group-A -m

Refer: http://manpages.ubuntu.com/manpages/trusty/man8/useradd.8.html


  1. Create additional skel directories.

    sudo mkdir /etc/skel{A,B}
    
  2. Copy the contents of /etc/skel to /etc/skelA.

    sudo cp /etc/skel/* /etc/skelA/
    
  3. Customize the alternative skel directory contents.

  4. adduser without a homedir, but with otherwise normal settings. Replace the ellipsis with the appropriate settings.

    sudo adduser --no-create-home ... bob
    
  5. mkhomedir_helper to create users homedir based on alternative skel dir.

    sudo mkhomedir_helper bob /etc/skelA
    
#!/bin/bash
### a bare bones dumb script to create a user with a homedir based on an alternitive skeldir
adduseropt="--no-create-home --ingroup"
# assumes $1 will be user TODO add sanity check
# Assumes $2 will be alternitive skeldir TODO add sanity check
# assumes $3 will be a group TODO add sanity check
sudo adduser --no-create-home $adduseropt $3 $1
sudo mkhomedir_helper $1 $2

adduser does support a limited way to exclude files from the skeleton directory. From man adduser.conf:

SKEL_IGNORE_REGEX
    Files  in  /etc/skel/  are  checked  against this regex, and not
    copied to the newly created home directory if they match.   This
    is  by default set to the regular expression matching files left
    over from unmerged config files (dpkg-(old|new|dist)).

While you can't set this regex from the command line, you can set the config file used using the --conf option. So you can create additional copies of /etc/adduser.conf that only differ on the SKEL_IGNORE_REGEX, and use those:

(grep -v '^SKEL_IGNORE_REGEX' /etc/adduser.conf; printf "%s\n" 'SKEL_IGNORE_REGEX="not_for_a.txt"') > /etc/adduser_A.txt
sudo adduser --conf /etc/adduser_A.txt ...

The adduser command can run a site-specific script to do any setup like removing files. As long as it is acceptable to start with a full copy and then delete some files afterwards, then this approach could work for you.

From the adduser(8) man page:

If the file /usr/local/sbin/adduser.local exists, it will be executed after the user account has been set up in order to do any local setup. The arguments passed to adduser.local are:

username uid gid home-directory

So all you need to do is write a script that takes four parameters and use it remove any files you need. Save it as /usr/local/sbin/adduser.local and make sure it's marked executable (chmod a+x).

Here's something to get you started:

#!/bin/bash
## Site-specific setup for newly-created users.
## adduser(8) will call this script after setting up a new user.

set -euo pipefail
if [[ "$#" != 4 ]]; then
  echo "usage: $0 username uid gid home" > /dev/stderr
fi
NEW_USERNAME="${1:?}"
NEW_UID="${2:?}"
NEW_GID="${3:?}"
NEW_HOME="${4:?}"

# The groups command outputs a space-separated list of group names
IFS=' '
for group in $(groups "${NEW_USERNAME}"); do
   case "${group}" in
     a)
       [[ "${VERBOSE}" > 0 ]] && echo Removing file for a
       rm "${NEW_HOME}/not_for_a.txt"
       ;;
     b)
       [[ "${VERBOSE}" > 0 ]] && echo Removing dir for b
       rm -r "${NEW_HOME}/not_for_b/"
       ;;
     *)
       [[ "${VERBOSE}" > 1 ]] && echo No special setup required for $group
       ;;
   esac
done

The interesting part, which you'll want to edit, are the lines that look like this one:

     a)
       [[ "${VERBOSE}" > 0 ]] && echo Removing file for a
       rm "${NEW_HOME}/not_for_a.txt"
       ;;

You can fill in the actual group name and behaviour you'd like to see instead of a) and rm not_for_a.txt.