Ansible: set user password only once?

Hi,

I have a local.yml playbook to setup a test environment with ansible-pull. In this playbook I have a setup_firstuser role that sets up the first non-root user.

Here’s what setup_firstuser/tasks/main.yml looks like:

- name: "Setup account and generate SSH key pair for initial user: \
         {{firstuser_login}}"
  ansible.builtin.user:
    name: "{{firstuser_login}}"
    comment: "{{firstuser_name}}"
    password: >-
      {{firstuser_passwd|
      password_hash('sha512', 65534|
      random(seed=inventory_hostname)|
      string)}}
    generate_ssh_key: true
    ssh_key_type: ed25519
    ssh_key_file: .ssh/id_ed25519

- name: "Define administrator rights for initial user: {{firstuser_login}}"
  ansible.builtin.user:
    name: "{{firstuser_login}}"
    groups: wheel,systemd-journal
  when: firstuser_admin

And here’s the corresponding setup_firstuser/defaults/main.yml :

firstuser_login: ema
firstuser_name: EMA
firstuser_passwd: # no default
firstuser_admin: true

In the beginning of the local.yml playbook I have a vars_prompt section that prompts the user for a password like this :

  vars_prompt:
      - name: firstuser_passwd
        prompt: Choose a password for user ema
        default: ema123
        private: true

This all works nicely so far, except every time the playbook runs, the password prompt appears. So the user either has to confirm the default password or retype his or her custom password.

Here’s what I’d like to do :

  • Only prompt for the password if it hasn’t already been set.
  • If there is already a password for the ema account then don’t show the prompt and leave the password as is.

Or maybe even better :

  • Define a dead simple default password (like ema123) for the user in the playbook.
  • Let the user change the password manually (using the passwd command).
  • Once there is a password, the playbook doesn’t try to set it anymore.

Any idea on how I could achieve that ?

Can we assume that existing account has password?
If so, there is a need to set password only if account does not exist when play starts.

One could get info about account:

  - name: Get first_user info
    ansible.builtin.getent:
      database: passwd
      key: "{{firstuser_login}}"

  - name: Show the info
    ansible.builtin.debug:
      var: ansible_facts.getent_passwd

Alas, the ansible.builtin.getent will fail if that account does not exist.
Hence, one should handle the failure somehow.

One can also get all accounts. E.g.

  - name: Get user info
    ansible.builtin.getent:
      database: passwd

  - name: Users
    ansible.builtin.debug:
      msg: "{{ item.key }} {{ item.value.1 }} {{ item.value.4 }}"
    loop: "{{ ansible_facts.getent_passwd | dict2items }}"
    when:
    - item.value.1|int < 1000
    loop_control:
      label: "{{ item.key }}"

and register/set something if firstuser_login is on the list.

Or perhaps just check whether the home directory for firstuser_login exists?


Once one has ‘has_account’ boolean, one can put the password-related tasks to a block.

has_account = ...

create/update account (but not password)

if has_account:
    ask password
    set password
1 Like

Thanks for the suggestion. I ended up finding a very simple solution. Ansible’s user module has an on_create parameter for the password. Add just this single line, and I have the expected behavior.

2 Likes