Add a new user with Cfengine 3

In my last blog post about Cfengine 3 we talked about how Cfengine 3 can check for you if an user exists or not.

Today we will have a look at a very simple way of how a new user can be created if it doesn’t already exist.

Writing the Cfengine 3 code for creating a new user

body common control {
        version         => "1.0";
        inputs          => { "cfengine_stdlib.cf" };
        bundlesequence  => { "check_user_exists_and_create_if_required" };
}

bundle agent check_user_exists_and_create_if_required {
        vars:
          "userNagiosData" string => "nagios -d /home/nagios -s /bin/bash -m -U";

        classes:
          "userNagiosExists" expression => userexists("nagios");

        reports:
          !userNagiosExists::
                "The user nagios does not exist and I will attempt to create it.";

        commands:
          !userNagiosExists::
                "/usr/sbin/useradd $(userNagiosData)";
}

Let’s put the code above in a new file and check it for errors. Afterwards it can be executed:

/var/cfengine/bin/cf-promises -f /etc/cfengine3/xenuser_org-016-add_new_user.cf 
/var/cfengine/bin/cf-agent -f /etc/cfengine3/xenuser_org-016-add_new_user.cf

When you now have a look at the syslog, Cfengine 3 should have issued a warning if the user nagios didn’t exist before running the Cfengine 3 snippet:

tail -f /var/log/syslog
Oct  7 13:41:11 mintbox cf3[3643]:  R: The user nagios does not exist and I will attempt to create it.

Let’s verify that Cfengine 3 really created our user:

getent passwd |grep nagios
nagios:x:1003:1003::/home/nagios:/bin/bash

ls -lah --color /home
drwxr-xr-x   8 root     root     4,0K 2012-10-07 13:42 .
drwxr-xr-x  23 root     root     4,0K 2012-08-20 22:04 ..
drwxr-xr-x   4 nagios   nagios   4,0K 2012-10-07 13:41 nagios
drwxr-xr-x  32 valentin valentin 4,0K 2012-10-07 13:27 valentin

As you can see everything turned out well.

Analyzing the Cfengine 3 code snippet
Since the last blog post already covered most parts of the Cfengine 3 code snippet above, we will only have a look at the new section:

        vars:
          "userNagiosData" string => "nagios -d /home/nagios -s /bin/bash -m -U";

In the code above we define a new promise of the type “vars” which contains a simple text variable. The variable “userNagiosData” only contains the parameters which will be provided to useradd later:

        commands:
          !userNagiosExists::
                "/usr/sbin/useradd $(userNagiosData)";

As you can see, the promise of the type “commands” checks if the condition “Does the user nagios already exist?” is met. If it is not met, the provided shell command will be executed. It is very simple: We are lazy and just run the command “useradd” with the parameters we defined at the beginning of the Cfengine 3 snippet.

Summary
Cfengine 3 provides very simple, put powerful ways of checking if an user already exists. Furthermore adding new users is very easy since we can run shell commands directly out of Cfengine 3. However, the example here is probably not very good. It would be better to split the tasks “checking if an user exist” and “creating a new user” into two different bundle agents. The bundle agent responsible for the user creating then can be equipped with various methods, e.g. for creating the new user not only on Linux systems, but also on Unix and Windows machines.

Another improvement could be to create an array which contains several users. The advantage would be that only one Cfengine 3 script has to be written for all users you want to add on the local system.

As usual, you can download today’s educational Cfengine 3 snippet here.