How to Create a Linux User Using Ansible

Automate Linux user creation with Ansible: install dependencies, generate hashed passwords with passlib, use the user and authorized_key modules, harden SSH, and run the playbook with an inventory file.

This guide shows how to automate Linux user creation with Ansible. You’ll define a playbook that creates a user with a hashed password, adds your SSH key for passwordless login, and optionally hardens SSH (disable root login, restrict allowed users) and adds the user to sudoers.

Ansible is an open-source automation platform for configuration management, application deployment, and infrastructure as code. It runs on Unix-like systems and can manage Linux, macOS, and Windows targets.

Prerequisites

  • Python 3 and pip on your control node (the machine where you run Ansible).
  • Ansible installed (see Installing Ansible).
  • SSH access to the target Linux server (e.g. as root or a user with sudo).
  • passlib (for generating password hashes compatible with Linux).

Overview

  1. Install dependencies (Ansible and passlib).
  2. Generate a hashed password with passlib.
  3. Create the playbook (user, optional SSH key, optional SSH/sudo hardening).
  4. Run the playbook using an inventory file.

1. Install dependencies

Install passlib (used to generate SHA-512 hashes for the user password) and Ansible:

1
2
pip install passlib
pip install ansible

Use pip install --user or a virtual environment if you prefer not to install globally. On many systems you can also install Ansible via the package manager (e.g. dnf install ansible-core on Fedora/RHEL).

2. Generate a hashed password

Ansible’s user module expects the password in hashed form (e.g. SHA-512). Plain text will not work. Generate a hash with Python and passlib:

1
python3 -c "from passlib.hash import sha512_crypt; import getpass; print(sha512_crypt.using(rounds=5000).hash(getpass.getpass()))"

You’ll be prompted for the password; the command prints the hash. Example output:

1
2
Password:
$6$rounds=5000$...$...

Copy the full hash; you’ll use it in the playbook vars.

3. Define the playbook

Start with the play definition: name, hosts, become (to run as root), and vars for the username and password hash:

1
2
3
4
5
6
7
- name: Create user on a linux server
  hosts: remote-server
  become: yes
  gather_facts: false
  vars:
    - user: <username here>
    - password: <password hash from step 2>

Then add the tasks below.

Task: Create the Linux user

Use the Ansible user module to create the user with the given name and hashed password, and add them to the wheel group (common for sudo on RHEL/Rocky/Alma):

1
2
3
4
5
6
7
- name: Create a login user
  ansible.builtin.user:
    name: "{{ user }}"
    password: "{{ password }}"
    groups:
      - wheel
    state: present

Optional: Add SSH public key for passwordless login

To log in via SSH without a password, add your public key to the user’s authorized_keys using the authorized_key module:

1
2
3
4
5
- name: Add public key to authorized_keys
  ansible.posix.authorized_key:
    user: "{{ user }}"
    state: present
    key: "{{ lookup('file', '~/.ssh/id_rsa.pub') }}"

Install the collection if needed: ansible-galaxy collection install ansible.posix.

Optional: Disable root SSH login

Restricting SSH to non-root users is a good security practice. Use lineinfile to set PermitRootLogin no in sshd_config:

1
2
3
4
5
6
- name: Deny root from logging in
  ansible.builtin.lineinfile:
    path: /etc/ssh/sshd_config
    regexp: '^(#)?PermitRootLogin \w*$'
    line: "PermitRootLogin no"
    state: present

After this change, restart SSH (e.g. systemctl restart sshd) or add an Ansible handler so the service restarts when the config changes.

Optional: Restrict SSH to allowed users

You can limit which users can log in over SSH with AllowUsers:

1
2
3
4
5
6
- name: Allow specific users to log in
  ansible.builtin.lineinfile:
    path: /etc/ssh/sshd_config
    regexp: "^AllowUsers"
    line: "AllowUsers {{ user }}"
    state: present

Optional: Add user to sudoers (passwordless sudo)

To let the user run sudo without a password prompt, add a line to sudoers. The validate option runs visudo -cf so the file is checked before saving:

1
2
3
4
5
6
- name: Add {{ user }} to sudoers file
  ansible.builtin.lineinfile:
    path: /etc/sudoers
    regexp: "^{{ user }}"
    line: "{{ user }} ALL=(ALL) NOPASSWD: ALL"
    validate: "visudo -cf %s"

4. Full playbook example

Save the following as create-user.yaml (or any .yml file). Replace user1 and the password hash with your values:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
---
- name: Create user on a linux server
  hosts: remote-server
  become: yes
  gather_facts: false
  vars:
    - user: user1
    - password: $6$rounds=5000$... # use the hash from step 2
  tasks:
    - name: Create a login user
      ansible.builtin.user:
        name: "{{ user }}"
        password: "{{ password }}"
        groups:
          - wheel
        state: present

    - name: Add public key to authorized_keys
      ansible.posix.authorized_key:
        user: "{{ user }}"
        state: present
        key: "{{ lookup('file', '~/.ssh/id_rsa.pub') }}"

    - name: Deny root from logging in
      ansible.builtin.lineinfile:
        path: /etc/ssh/sshd_config
        regexp: '^(#)?PermitRootLogin \w*$'
        line: "PermitRootLogin no"
        state: present

    - name: Allow specific users to log in
      ansible.builtin.lineinfile:
        path: /etc/ssh/sshd_config
        regexp: "^AllowUsers"
        line: "AllowUsers {{ user }}"
        state: present

    - name: Add {{ user }} to sudoers file
      ansible.builtin.lineinfile:
        path: /etc/sudoers
        regexp: "^{{ user }}"
        line: "{{ user }} ALL=(ALL) NOPASSWD: ALL"
        validate: "visudo -cf %s"

Note: Do not commit real password hashes or production IPs to version control. Use Ansible Vault, environment variables, or a secrets manager for sensitive values.

5. Run the playbook

Create an inventory file that defines remote-server (the host name used in the playbook). Example hosts.yaml:

1
2
3
4
5
all:
  hosts:
    remote-server:
      ansible_host: 192.0.2.10 # replace with your server IP or hostname
      ansible_user: root

Modern Ansible uses ansible_host and ansible_user; older docs may show ansible_ssh_host / ansible_ssh_user, which also work.

Run the playbook:

1
ansible-playbook -i hosts.yaml create-user.yaml -v

Use -v, -vv, or -vvv for more verbose output. Example recap when everything succeeds:

1
2
PLAY RECAP ********************************************************************
remote-server : ok=5  changed=4  unreachable=0  failed=0  skipped=0  rescued=0  ignored=0

If you changed sshd_config, restart SSH on the target (e.g. systemctl restart sshd) or add a handler in the playbook so the service restarts when the config file changes.

Summary

You installed Ansible and passlib, generated a SHA-512 password hash, and built a playbook that creates a Linux user (with optional SSH key, SSH hardening, and sudo access). Run it with ansible-playbook -i inventory create-user.yaml. For more, see the Ansible user module, authorized_key module, and lineinfile documentation.

comments powered by Disqus
Citizix Ltd
Built with Hugo
Theme Stack designed by Jimmy