Skip to main content

4 steps to create Linux users from a csv file with Ansible

Automate Linux user account creation in four simple steps with Ansible.
Image
Using Ansible to create users from a CSV file

"user accounts" by trendingtopics is licensed under CC BY 2.0

Ansible has made automation simple and it has become the universal automation language. By now, the benefits are well-known in the IT community. Like all good technologies, however, there can be challenges to face or approaches to combine. Consider using comma-separated value (CSV) files and Ansible to create Linux user accounts.

In this article, the goal is to automate user creation using Ansible. A list of users can be passed via an external variable file using vars_files or directly into the playbook using a loop. The problem arises when the list of users to be created is only available in a format like CSV while the Ansible developer is more comfortable using a YAML list. I personally have experienced situations where a user list including passwords is provided in a CSV file with the requirement to create those users on multiple Linux machines using an automation tool such as Ansible.

[ You might also like: Automate virtual machine deployment with Ansible: Design ]

What is the solution?

Fortunately, Ansible ships with more than a thousand modules, including one for reading CSV files. This module can provide exactly what's needed for this task. You'll see the module in action below.

Consider the CSV file that includes multiple fields such as Username, UID, First Names, Last Names, Groups, and Password shown below. The requirement is to create all these users with their relevant information across multiple Linux servers. 

Username,UID,First_name,Last_name,Groups,Password
booker12,9012,Rachel,Booker,Operations,iambooker
grey07,2070,Laura,Grey,Developers,iamgrey
johnson81,4081,Craig,Johnson,Operations,iamjohnson
jenkins46,9346,Mary,Jenkins,Developers,iamjenkins
smith79,5079,Jamie,Smith,Operations,iamsmith

Step 1

Now that you have a CSV file, the next step is to get Ansible to read this CSV file. To achieve this, use the read_csv module. To print how Ansible has parsed the CSV, use the debug module. Notice the use of delegate_to: localhost in the first task. This is because the file username.csv is present on the control-node and not the managed-hosts. Ansible parses this CSV in a list format with multiple dictionaries inside. That is why you need to add the sub-variable list to the end of your original registered variable user_list.

---
- name: create users from csv file
  hosts: all
  tasks:
   - name: reading the csv file
     read_csv:
      path: username.csv
     register: user_list
     delegate_to: localhost

   - name: display user_list data
     debug:
      var: user_list.list

Step 2

As you run this playbook, you'll see that the output contains a list, as indicated by the pair of box brackets. This list further includes a dictionary, which is indicated by the curly brackets. Every row in the CSV file has been converted into a dictionary, based on the comma separation between them. A dictionary is used to store data in a pair of keys and values. For example, the key is First_name, and the value is Rachel.

ok: [192.168.0.3] => {
    "user_list.list": [
        {
            "First_name": "Rachel",
            "Groups": "Operations",
            "Last_name": "Booker",
            "Password": "iambooker",
            "UID": "9012",
            "Username": "booker12"
        },
        {
            "First_name": "Laura",
            "Groups": "Developers",
            "Last_name": "Grey",
            "Password": "iamgrey",
            "UID": "2070",
            "Username": "grey07"
        },
        {
            "First_name": "Craig",
            "Groups": "Operations",
            "Last_name": "Johnson",
            "Password": "iamjohnson",
            "UID": "4081",
            "Username": "johnson81"
        },
      -------OUTPUT OMITTED-------
        }
    ]
}

Step 3

You're successfully able to read and print the CSV file via Ansible. Now it's time to try extracting one of these values to see if you can manipulate this dictionary to suit the needs of the user module in Ansible. Try something basic like extracting the value of the Username key for all the dictionaries inside this list. To do this, you'll use the dictionary's Username key as a sub-variable for your Ansible loop.

----OUTPUT OMITTED----
- name: extract Username from all dictionaries
  debug:
    msg: "{{ item.Username }}"
    loop: "{{ user_list.list }}"

ok: [192.168.0.3] => (item={'Username': 'booker12', 'UID': '9012', 'First_name': 'Rachel', 'Last_name': 'Booker', 'Groups': 'Operations', 'Password': 'iambooker'}) => {
    "msg": "booker12"
}
ok: [192.168.0.3] => (item={'Username': 'grey07', 'UID': '2070', 'First_name': 'Laura', 'Last_name': 'Grey', 'Groups': 'Developers', 'Password': 'iamgrey'}) => {
    "msg": "grey07"
}
----OUTPUT OMITTED----

As you can see here, you've extracted the Username values from the dictionaries. Using the same concept, you can put these values in the name field of the user module to create the users.

Step 4

Now that you've extracted the value of the desired key, you should be able to work with all the key and value pairs. Put them in the user module to create your users with all the required information. Notice the password option of the user module. It contains a filter to encrypt the Password value using the SHA-512 algorithm. This is done because RHEL8 uses SHA-512 to encrypt user passwords. This password is stored in an encrypted format in the /etc/shadow file on the managed-hosts. Also, the comment option contains two variables because you want the GECOS (User comments) field of the user to have their first name and last name. For example, the user booker12 will have their GECOS as "Rachel Booker." Furthermore, you're using privilege escalation in this task because user creation requires root privileges.

-----OUTPUT OMITTED-----  
- name: create users from the csv
  user:
    name: "{{ item.Username }}"
    uid: "{{ item.UID }}"
    groups: "{{ item.Groups }}"
    append: true
    password: "{{ item.Password | password_hash('sha512') }}"
    comment: "{{ item.First_Name }} {{ item.Last_Name }}"
    state: present
  loop: "{{ user_list.list }}"
  become: true

[ A free course for you: Virtualization and Infrastructure Migration Technical Overview. ] 

Wrap up

You successfully created the given users with their UID, Groups, Password, and GECOS by extracting the desired values from a CSV file. This comes in handy because most organizations use formats such as CSV to store and manage their data. An Ansible developer who can manipulate CSV files can efficiently manage their environment without writing everything from scratch directly into the playbook or an external variable YAML file. To effectively manage your environment further, I would recommend using Ansible Tower, which is the enterprise Ansible solution offered by Red Hat. Perhaps go for the DO447 - Advanced Automation: Ansible Best Practices training offering, as well.

Topics:   Linux   Linux administration   Ansible   Automation  
Author’s photo

Aryan Srivastava

Technological consultant and instructor for a Red Hat training partner based in India. Works on Ansible Tower, Red Hat Satellite, Python, and Ethical Hacking. Experience in delivering security and automation solutions as per business needs. More about me

Try Red Hat Enterprise Linux

Download it at no charge from the Red Hat Developer program.