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 ?

I won’t bet on that.
One way to go is to expire the password, once the user is created. That would force the user to change their password, once it logins via default password.
But afaik, if you setup expiration once, it lasts forevery (when you don’t deactivate it again). So one day, the user will be forced to update their password again.

Another possibility is to replace the var_prompt at the beginning with a

    - name: > 
         Check if user 'alf' exists
         yes, relaying just on the home directoy is not safety
      stat:
        path: /home/alf
        follow_symlink: no

    - name: register new password of user does not exists
      when: not ansible_stat.stat.exists
      pause:
        prompt: enter password for user alf
      register: prompt_password
      no_log: true

Also there is update_password: on_create, that won’t update the passwort if the user exists already.

In case of a remote enviroment - don’t relay on password authentification. Just fetch some public keys from github/gitlab/gitea

- name: fetch ssh public keys
  ansible.posix.authorized_key:
    user: "{{ ansible_user_id }}"
    state: present
    manage_dir: true
    exclusive: true
    key: https://github.com/markuman.keys
2 Likes

There are lots of ways to do this, as @markuman has said above the best option is to not set a password and install SSH public keys for the account, if a password is essential then I have an Ansible role to generate a random password and send a email to the users email address (depends on the machine having working outgoing email) with the password in it and also to write a file to the server with the date and time the email was sent and if this file exists the role skips the tasks, so to reset the password you can delete the file and re-run the role.

1 Like

Use the update_password parameter and set it to on_create. This is designed for exactly what you want. It only sets the password if it is creating the user, otherwise it never updates it.

3 Likes

Worked like a charm with on_create.

Thanks everybody !