Introduction
The ansible.builtin.user module manages user accounts on Linux — create users, set passwords, configure groups, manage SSH keys, set shells, and remove accounts. It wraps useradd, usermod, and userdel with idempotent, cross-platform behavior.
Parameters
| Parameter | Default | Description |
|---|---|---|
name | (required) | Username |
state | present | present to create/modify, absent to remove |
uid | — | User ID number |
group | — | Primary group |
groups | — | Secondary groups (comma-separated or list) |
append | false | Append to groups (vs replace) |
shell | /bin/bash | Login shell |
home | /home/<name> | Home directory path |
create_home | true | Create home directory |
password | — | Hashed password (NOT plaintext!) |
comment | — | GECOS field (full name/description) |
system | false | Create system user (UID < 1000) |
expires | — | Account expiry (epoch timestamp) |
generate_ssh_key | false | Generate SSH keypair |
ssh_key_bits | 4096 | SSH key size |
ssh_key_type | rsa | Key type (rsa, ed25519, ecdsa) |
remove | false | Remove home dir when state: absent |
force | false | Force removal even if logged in |
Create a User
---
- name: Create users
hosts: all
become: true
tasks:
- name: Create basic user
ansible.builtin.user:
name: alice
comment: "Alice Smith"
shell: /bin/bash
state: present
- name: Create user with specific UID and groups
ansible.builtin.user:
name: bob
uid: 1500
group: developers
groups: docker,sudo
append: true
comment: "Bob Jones"
- name: Create system user (for services)
ansible.builtin.user:
name: myapp
system: true
shell: /usr/sbin/nologin
home: /opt/myapp
create_home: true
comment: "MyApp Service Account"
Set Password
⚠️ Passwords must be hashed, not plaintext:
---
- name: Create user with password
hosts: all
become: true
tasks:
# Generate hash with: ansible all -m debug -a "msg={{ 'mypassword' | password_hash('sha512') }}"
- name: Create user with hashed password
ansible.builtin.user:
name: alice
password: "{{ 'SecurePass123!' | password_hash('sha512') }}"
update_password: on_create # Only set password when creating user
# Using vault-encrypted variable
- name: Create user with vault password
ansible.builtin.user:
name: bob
password: "{{ vault_bob_password | password_hash('sha512', 'salt123') }}"
update_password: always # Update password every run
update_password Options
| Value | Behavior |
|---|---|
always (default) | Set password every run (changes if different) |
on_create | Only set password when user is first created |
Manage Groups
---
- name: Group management
hosts: all
become: true
tasks:
# ⚠️ WITHOUT append — REPLACES all secondary groups
- name: DANGEROUS — removes from all other groups
ansible.builtin.user:
name: alice
groups: docker
# append: false (default!)
# ✅ WITH append — adds to existing groups
- name: SAFE — add to docker group
ansible.builtin.user:
name: alice
groups: docker
append: true
# Multiple groups
- name: Add to multiple groups
ansible.builtin.user:
name: alice
groups:
- docker
- sudo
- developers
append: true
# Change primary group
- name: Set primary group
ansible.builtin.user:
name: alice
group: developers # Primary group (not groups!)
Generate SSH Key
---
- name: Create user with SSH key
hosts: all
become: true
tasks:
- name: Create user and generate SSH key
ansible.builtin.user:
name: deploy
generate_ssh_key: true
ssh_key_type: ed25519
ssh_key_comment: "deploy@{{ inventory_hostname }}"
ssh_key_file: .ssh/id_ed25519
register: user_result
- name: Show public key
ansible.builtin.debug:
msg: "{{ user_result.ssh_public_key }}"
# Deploy authorized_keys
- name: Add authorized key
ansible.posix.authorized_key:
user: deploy
key: "{{ lookup('file', '~/.ssh/deploy_key.pub') }}"
state: present
Create Multiple Users
---
- name: Bulk user management
hosts: all
become: true
vars:
users:
- name: alice
comment: "Alice Smith"
groups: [developers, docker]
shell: /bin/bash
- name: bob
comment: "Bob Jones"
groups: [developers, sudo]
shell: /bin/bash
- name: charlie
comment: "Charlie Brown"
groups: [dbadmins]
shell: /bin/zsh
- name: deploy
comment: "Deploy Account"
groups: [deployers]
shell: /bin/bash
generate_ssh_key: true
tasks:
- name: Create all users
ansible.builtin.user:
name: "{{ item.name }}"
comment: "{{ item.comment }}"
groups: "{{ item.groups }}"
shell: "{{ item.shell | default('/bin/bash') }}"
append: true
generate_ssh_key: "{{ item.generate_ssh_key | default(false) }}"
state: present
loop: "{{ users }}"
- name: Deploy SSH authorized keys
ansible.posix.authorized_key:
user: "{{ item.name }}"
key: "{{ lookup('file', 'ssh_keys/' + item.name + '.pub') }}"
loop: "{{ users }}"
when: "'ssh_keys/' + item.name + '.pub' is file"
ignore_errors: true
Delete a User
- name: Remove user account
ansible.builtin.user:
name: olduser
state: absent
remove: true # Also remove home directory
force: true # Force even if user is logged in
Practical Patterns
Service Account for Application
---
- name: Set up application
hosts: appservers
become: true
tasks:
- name: Create app group
ansible.builtin.group:
name: myapp
system: true
- name: Create service account
ansible.builtin.user:
name: myapp
group: myapp
system: true
shell: /usr/sbin/nologin
home: /opt/myapp
create_home: true
comment: "MyApp Service"
- name: Set directory permissions
ansible.builtin.file:
path: "{{ item }}"
state: directory
owner: myapp
group: myapp
mode: "0755"
loop:
- /opt/myapp
- /opt/myapp/data
- /var/log/myapp
Lock/Unlock User
# Lock account (disable login)
- ansible.builtin.user:
name: alice
password_lock: true
# Unlock account
- ansible.builtin.user:
name: alice
password_lock: false
# Set account expiry
- ansible.builtin.user:
name: contractor
expires: "{{ ('2026-12-31' | to_datetime).strftime('%s') }}"
Change Shell
- name: Set zsh for developers
ansible.builtin.user:
name: "{{ item }}"
shell: /bin/zsh
loop: "{{ developers }}"
Troubleshooting
"useradd: group 'mygroup' does not exist"
Create the group first:
- ansible.builtin.group:
name: mygroup
state: present
- ansible.builtin.user:
name: alice
group: mygroup
Password Not Working
Passwords must be hashed. Plaintext won't work:
# WRONG — plaintext password
password: "mypassword"
# RIGHT — hashed password
password: "{{ 'mypassword' | password_hash('sha512') }}"
User Removed from Groups
You forgot append: true:
# This REMOVES alice from all groups except docker
- ansible.builtin.user:
name: alice
groups: docker
# This ADDS docker to alice's existing groups
- ansible.builtin.user:
name: alice
groups: docker
append: true # ← Critical!
Related Articles
- Ansible group Module
- Linux Users and Groups Guide
- Ansible authorized_key Module
- Ansible File Permissions
- Ansible Create Directory
Conclusion
ansible.builtin.user handles full lifecycle user management — creation, password hashing, group membership, SSH keys, and deletion. The most common mistake: forgetting append: true when adding groups (without it, all other group memberships are removed). Use update_password: on_create to avoid resetting passwords on every run. For service accounts, combine system: true with shell: /usr/sbin/nologin for security.