Adding and modifying users with Ansible

Whether you are configuring a brand new server for the first time after it has been built or making changes when a new employee joins the company, adding user accounts to a server is a commonly required task. Thankfully, Ansible has a module called user, which is designed to perform user account management tasks, and we shall proceed to use exactly this.

Throughout our previous examples, we have been very careful to highlight the differences between platforms such as Ubuntu and CentOS, and user account management requires a little consideration here too.

Take, for example, the following shell command (which we will later automate in Ansible):

$ useradd -c "John Doe" -s /bin/bash johndoe 

This command could be run on either CentOS 7 or Ubuntu Server 18.04, and would yield the same results, namely:

  • The user account johndoe would be added with the next free user identification number (UID) for users.
  • The account comment would be set to John Doe
  • The shell would be set to /bin/bash . 

Indeed, you could run this command on just about any Linux system, and it would work. The differences start, however, when you consider groups, especially built-in ones. For example, if you wanted this account to be able to use sudo for root access (that is, johndoe is a system administrator), you would want to put this account into the wheel group on CentOS 7. On Ubuntu Server, however, there is no wheel group, and attempting to put the user into such a group would result in an error. Instead, on Ubuntu, this user would go into the sudo group.

It is subtle differences like this that could trip you up when it comes to automated user account management across different Linux distributions—however, as long as you remain mindful of such things, you can easily create Ansible playbooks or roles, to manage your Linux users with ease.

Let's build on this example, to instead create the johndoe user in an Ansible role, such that access for them can be rolled out on all Linux servers. The code for roles/addusers/tasks/main.yml to perform the same function as the shell of the preceding command should look something like the following:

---
- name: Add required users to Linux servers
user:
name: johndoe
comment: John Doe
shell: /bin/bash

If we run this role in the usual way, we can see that the user account gets created on the first run, and that no action is taken if we run the playbook a second time. This is denoted in the following screenshot, which shows the preceding role being run twice—the changed and ok statuses show when a user account is added, and when no action is taken because it already exists respectively:

So far, so good—however, this example is rather skeletal in nature—our user has no password set, no group membership, and no authorized SSH keys. We demonstrated previously that we can run an Ansible role containing the user module more than once and changes will only be made if required, and we can leverage this to our advantage. Let's now expand our example role, to add these things.

Before we get into our next example, we will demonstrate how to generate a password hash, using Ansible. Here, we will choose the word secure123. The user module of Ansible is capable of setting and modifying user account passwords, but it does not (for very good reasons) allow you to specify the password in plaintext. Instead, you must create a password hash, to send to the machine being configured. In Chapter 6, Custom Builds with PXE Booting, we looked at a way to do this with a small amount of Python code, and you are welcome to reuse this method here. However, you can also make use of Ansible's vast array of filters, to generate a password hash from a string. Run the following command from the shell:

$ ansible localhost -i localhost, -m debug -a "msg={{ 'secure123' | password_hash('sha512') }}"

Running this produces a password hash that you can copy and paste into your role, as shown in the following screenshot:

This is very useful in itself—however, let's bear something in mind: no password hash is completely secure. Remember that once, MD5 hashes were considered secure, but are now not. Ideally, you should not be storing the hash in plaintext either, and should regenerate it on every system as it contains a unique salt. Luckily, we can use the password_hash filter in a role directly to achieve this. 

In the following example, we demonstrate how to store the password string in a variable, and then, how to use the password_hash filter to generate the hash for the remote system. In a real-world use case, you would replace the plaintext variable file with an Ansible vault file so that at no point is either the original password or hash stored unencrypted. 

  1. First of all, let's create roles/addusers/vars/main.yml, and store John Doe's password in a variable, as follows:
---
johndoepw: secure123
  1. Next, let's create an SSH key pair for this user, in the directory roles/addusers/files/, by running the following command in that directory:
$ ssh-keygen -b 2048 -t rsa -f ./johndoe_id_rsa -q -N ''

Of course, it is likely in an enterprise setting that the user would generate their own key pair and provide an administrator with the public key for distribution to the systems they will use—however, for our example here, it is easier to demonstrate with a newly generated key pair.

  1. Finally, let's say that johndoe is going to administer Ubuntu systems, and so, should be in the sudo group. Our resulting role should now look like this:
---
- name: Add required users to Linux servers
user:
name: johndoe
comment: John Doe
shell: /bin/bash
groups: sudo
append: yes
password: "{{ johndoepw | password_hash('sha512') }}"

- name: Add user's SSH public key
authorized_key:
user: johndoe
state: present
key: "{{ lookup('file', 'files/johndoe_id_rsa.pub') }}"
  1. Running the code yields changed results, as we would expect, and the following screenshot shows the successful addition of the user and their corresponding SSH public key:

Note that we have successfully modified the johndoe account here, as we created it earlier in this section—however, we could also have run this most recent role before the account creation, and the end result would have been the same. That is the beauty of Ansible—you don't need to write different code for modifications and additions. There are many other modifications possible with the user module, and it should serve most of your needs.

Returning briefly to the vars/main.yml file we created earlier, we left this in plaintext for simplicity in this example. However, we can very easily encrypt our existing file, using the following command:

$ ansible-vault encrypt main.yml

The following screenshot shows this encryption process in action:

The data is now encrypted at rest! We can still run the playbook without decrypting it—simply add the --ask-vault-pass parameter to the ansible-playbook command, and enter your chosen vault password when prompted. 

Before concluding this section, it is worth noting that we can also leverage loops, to create multiple accounts at once. The following example creates two new users with differing group membership, and with distinct username and matching comments on their accounts. Expanding this example to address initial passwords and/or SSH keys is left as an exercise for you, but you should have enough information to build upon to achieve this. The code can be seen below:

---
- name: Add required users to Linux servers
user:
name: "{{ item.name }}"
comment: "{{ item.comment }}"
shell: /bin/bash
groups: "{{ item.groups }}"
append: yes
state: present
loop:
- { name: 'johndoe', comment: 'John Doe', groups: 'sudo'}
- { name: 'janedoe', comment: 'Jane Doe', groups: 'docker'}

Noting that we created johndoe earlier in this chapter, we can see that if we run this role, the janedoe user is the only account created as they did not already exist—the following screenshot shows exactly this. janedoe shows a changed status, informing us that a change was made—in this case, the account was created. The ok status against the johndoe user account tells us that no action was performed, as can be seen in the following screenshot:

In this way, user accounts can be created and managed at scale, across a wide number of Linux servers. As we can see in the preceding screenshot, in the usual Ansible manner, only the required changes are made, with existing accounts left unchanged. While adding accounts is straightforward, we must also consider that employees also leave enterprises from time to time, and so, account cleanup is also required in this instance.

We will explore the ways in which Ansible can assist with removing user accounts and tidying up after them, in the next section.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset