Creating a new user and password with Ansible

BashShellUbuntuAnsible

Bash Problem Overview


I have an ansible task which creates a new user on ubuntu 12.04;

- name: Add deployment user
    action: user name=deployer password=mypassword

it completes as expected but when I login as that user and try to sudo with the password I set it always says it's incorrect. What am I doing wrong?

Bash Solutions


Solution 1 - Bash

I may be too late to reply this but recently I figured out that jinja2 filters have the capability to handle the generation of encrypted passwords. In my main.yml I'm generating the encrypted password as:

- name: Creating user "{{ uusername }}" with admin access
  user: 
    name: {{ uusername }}
    password: {{ upassword | password_hash('sha512') }}
    groups: admin append=yes
  when:  assigned_role  == "yes"

- name: Creating users "{{ uusername }}" without admin access
  user:
    name: {{ uusername }}
    password: {{ upassword | password_hash('sha512') }}
  when:  assigned_role == "no"

- name: Expiring password for user "{{ uusername }}"
  shell: chage -d 0 "{{ uusername }}"

"uusername " and "upassword " are passed as --extra-vars to the playbook and notice I have used jinja2 filter here to encrypt the passed password.

I have added below tutorial related to this to my blog

Solution 2 - Bash

If you read Ansible's manual for user module, it'll direct you to the Ansible-examples github repo for details how to use password parameter.

There you'll see that your password must be hashed.

- hosts: all
  user: root
  vars:
    # created with:
    # python -c 'import crypt; print crypt.crypt("This is my Password", "$1$SomeSalt$")'
    password: $1$SomeSalt$UqddPX3r4kH3UL5jq5/ZI.

  tasks:
    - user: name=tset password={{password}}

If your playbook or ansible command line has your password as-is in plain text, this means your password hash recorded in your shadow file is wrong. That means when you try to authenticate with your password its hash will never match.

Additionally, see Ansible FAQ regarding some nuances of password parameter and how to correctly use it.

Solution 3 - Bash

I want to propose yet another solution:

- name: Create madhead user
  user:
    name: madhead
    password: "{{ 'password' | password_hash('sha512') }}"
    shell: /bin/zsh
    update_password: on_create
  register: madhead
- name: Force madhead to change password
  shell: chage -d 0 madhead
  when: madhead.changed

Why it is better? Like already has been noted here, Ansible plays should be idempotent. You should think of them not as a sequence of actions in imperative style, but like a desired state, declarative style. As a result you should be able to run it multiple times and get the same result, the same server state.

This all sounds great, but there are some nuances. One of them is managing users. "Desired state" means that every time you run a play that creates a user he will be updated to match exactly that state. By "updated" I mean that his password will be changed too. But most probably it is not what you need. Usually, you need to create user, set and expire his password only once, further play runs shouldn't update his password.

Fortunately, Ansible has update_password attribute in user module that solves this issue. Mixing this with registered variables you can also expire his password only when the user is actually updated.

Note that if you change user's shell manually (suppose, you don't like the shell that evil admin forced in his play) the user will be updated, thus his password will be expired.

Also note how you can easily use plain text initial passwords in plays. No need to encode them somewhere else and paste hashes, you can use Jinja2 filter for that. However, this can be a security flaw if someone happens to login before you initially do.

Solution 4 - Bash

try like this

vars_prompt:
 - name: "user_password"    
   prompt: "Enter a password for the user"    
   private: yes    
   encrypt: "md5_crypt" #need to have python-passlib installed in local machine before we can use it    
   confirm: yes    
   salt_size: 7

 - name: "add new user" user: name="{{user_name}}" comment="{{description_user}}" password="{{user_password}}" home="{{home_dir}}" shell="/bin/bash"

Solution 5 - Bash

The Ansible 'user' module manages users, in the idempotent way. In the playbook below the first task declares state=present for the user. Note that 'register: newuser' in the first action helps the second action to determine if the user is new (newuser.changed==True) or existing (newuser.changed==False), to only generate the password once.

The Ansible playbook has:

tasks:
  - name: create deployment user
    user: 
      name: deployer 
      createhome: yes 
      state: present 
    register: newuser

  - name: generate random password for user only on creation
    shell: /usr/bin/openssl rand -base64 32 | passwd --stdin deployer
    when: newuser.changed

Solution 6 - Bash

The purpose of the role in this answer is to generate random password for new_user_name and expire the password immediately. The new_user_name is required to change the password on his/her first logon.

create_user.yml:

---
# create_user playbook

- hosts: your_host_group
  become: True
  user: ansible

  roles:
    - create_user

roles/create_user/tasks/main.yml:

---
# Generate random password for new_user_name and the new_user_name
# is required to change his/her password on first logon. 

- name: Generate password for new user
  shell: makepasswd --chars=20
  register: user_password

- name: Generate encrypted password
  shell: mkpasswd --method=SHA-512 {{ user_password.stdout }}
  register: encrypted_user_password

- name: Create user account
  user: name={{ new_user_name }}
        password={{ encrypted_user_password.stdout }}
        state=present
        append=yes
        shell="/bin/bash"
        update_password=always
  when: new_user_name is defined and new_user_name in uids
  register: user_created

- name: Force user to change password
  shell: chage -d 0 {{ new_user_name }}
  when: user_created.changed

- name: User created
  debug: msg="Password for {{ new_user_name }} is {{ user_password.stdout }}"
  when: user_created.changed

When you want to create a new user:

ansible-playbook -i hosts.ini create_user.yml --extra-vars "new_user_name=kelvin"

Solution 7 - Bash

I tried many utilities including mkpasswd, Python, etc., but it seems like there is some compatibility issue with Ansible in reading HASH values generated by other tools. So finally it worked by Ansible # value itself.

ansible all -i localhost, -m debug -a "msg={{ 'yourpasswd' | password_hash('sha512', 'mysecretsalt') }}"

Playbook:

- name: User creation
  user: 
    name: username  
	uid: UID
	group: grpname
	shell: /bin/bash
	comment: "test user"
    password: "$6$mysecretsalt$1SMjoVXjYf.3sJR3a1WUxlDCmdJwC613.SUD4DOf40ASDFASJHASDFCDDDWERWEYbs8G00NHmOg29E0"

Solution 8 - Bash

My solution is using lookup and generate password automatically.

---
- hosts: 'all'
  remote_user: root
  gather_facts: no
  vars:
    deploy_user: deploy
    deploy_password: "{{ lookup('password', '/tmp/password chars=ascii_letters') }}"

  tasks:
    - name: Create deploy user
      user:
        name: "{{ deploy_user }}"
        password: "{{ deploy_password | password_hash('sha512') }}"

Solution 9 - Bash

This is how it worked for me

- hosts: main
  vars:
  # created with:
  #  python -c "from passlib.hash import sha512_crypt; print sha512_crypt.encrypt('<password>')"
  # above command requires the PassLib library: sudo pip install passlib
  - password: '$6$rounds=100000$H/83rErWaObIruDw$DEX.DgAuZuuF.wOyCjGHnVqIetVt3qRDnTUvLJHBFKdYr29uVYbfXJeHg.IacaEQ08WaHo9xCsJQgfgZjqGZI0'

tasks:

- user: name=spree password={{password}} groups=sudo,www-data shell=/bin/bash append=yes
  sudo: yes

Solution 10 - Bash

This is the easy way:

---
- name: Create user
  user: name=user shell=/bin/bash home=/srv/user groups=admin,sudo generate_ssh_key=yes ssh_key_bits=2048
- name: Set password to user
  shell: echo user:plain_text_password | sudo chpasswd
  no_log: True

Solution 11 - Bash

Just for completeness I will post the ad-hoc command using ansible since there is a catch there as well.

First try generating an encrypted password using the mkpasswd utility that is available on most Linux systems:

mkpasswd --method=SHA-512

Then try the ansible ad-hock command:

ansible all -m user -a 'name=testuser shell=/bin/bash \
     comment="Test User" password=$6$XXXX' -k -u admin --sudo

But make sure:

  1. The command is in single quotes and NOT double otherwise your password will never work
  2. You run it with --sudo or you end up with an error like (useradd: cannot lock /etc/passwd; try again later)

Solution 12 - Bash

Generating random password for user

first need to define users variable then follow below

tasks:

- name: Generate Passwords
  become: no
  local_action: command pwgen -N 1 8
  with_items: '{{ users }}'
  register: user_passwords

- name: Update User Passwords
  user:
    name: '{{ item.item }}'
    password: "{{ item.stdout | password_hash('sha512')}}"
    update_password: on_create
  with_items: '{{ user_passwords.results }}'

- name: Save Passwords Locally
  become: no
  local_action: copy content={{ item.stdout }} dest=./{{ item.item }}.txt
  with_items: '{{ user_passwords.results }}'

Solution 13 - Bash

The task definition for the user module should be different in the latest Ansible version.

tasks:
  - user: name=test password={{ password }} state=present

Solution 14 - Bash

How to create encrypted password for passing to password var to Ansible user task (from @Brendan Wood's comment):

openssl passwd -salt 'some_plain_salt' -1 'some_plain_pass'

The result will look like:

$1$some_pla$lmVKJwdV3Baf.o.F0OOy71

Example of user task:

- name: Create user
  user: name="my_user" password="$1$some_pla$lmVKJwdV3Baf.o.F0OOy71"

UPD: crypt using SHA-512 see here and here:

Python
$ python -c "import crypt, getpass, pwd; print crypt.crypt('password', '\$6\$saltsalt\$')"

$6$saltsalt$qFmFH.bQmmtXzyBY0s9v7Oicd2z4XSIecDzlB5KiA2/jctKu9YterLp8wwnSq.qc.eoxqOmSuNp2xS0ktL3nh/
Perl
$ perl -e 'print crypt("password","\$6\$saltsalt\$") . "\n"'

$6$saltsalt$qFmFH.bQmmtXzyBY0s9v7Oicd2z4XSIecDzlB5KiA2/jctKu9YterLp8wwnSq.qc.eoxqOmSuNp2xS0ktL3nh/
Ruby
$ ruby -e 'puts "password".crypt("$6$saltsalt$")'

$6$saltsalt$qFmFH.bQmmtXzyBY0s9v7Oicd2z4XSIecDzlB5KiA2/jctKu9YterLp8wwnSq.qc.eoxqOmSuNp2xS0ktL3nh/

Solution 15 - Bash

Combining a few solutions from above, I created a playbook that automatically generates correct password hashes based on plaintext passwords stored in an encrypted, local ansible vault file:

---
- hosts: [your hosts]
  tasks:
  - include_vars: [path to your encrypted vault file]
  - local_action: "command openssl passwd -salt '{{password_salt}}' -1 '{{password}}'"
    register: password_hash
  - user: >
        name=[your username]
        state=present
        password="{{password_hash.stdout}}"

Run this command using "--ask-vault-pass" option to decrypt your vault file (see ansible-vault for info on how to manage an encrypted vault).

Solution 16 - Bash

You can use ansible-vault for using secret keys in playbooks. Define your password in yml.

ex. pass: secret or

user:
  pass: secret
  name: fake

encrypt your secrets file with :

ansible-vault encrypt /path/to/credential.yml

ansible will ask a password for encrypt it. (i will explain how to use that pass)

And then you can use your variables where you want. No one can read them without vault-key.

Vault key usage:

via passing argument when running playbook.

--ask-vault-pass: secret

or you can save into file like password.txt and hide somewhere. (useful for CI users)

--vault-password-file=/path/to/file.txt

In your case : include vars yml and use your variables.

- include_vars: /path/credential.yml

  - name: Add deployment user
    action: user name={{user.name}} password={{user.pass}}

Solution 17 - Bash

Mxx's answer is correct but you the python crypt.crypt() method is not safe when different operating systems are involved (related to glibc hash algorithm used on your system.)

For example, It won't work if your generate your hash from MacOS and run a playbook on linux. In such case , You can use passlib (pip install passlib to install locally).

from passlib.hash import md5_crypt
python -c 'import crypt; print md5_crypt.encrypt("This is my Password,salt="SomeSalt")'
'$1$SomeSalt$UqddPX3r4kH3UL5jq5/ZI.'

Solution 18 - Bash

Neither of the solutions worked directly on my Mac controlling Ubuntu. So for others' sake, combining Mxx and JoelB answers, here is the current Python 3 solution:

pip3 install passlib

python3 -c 'from passlib.hash import md5_crypt; \
      print(md5_crypt.encrypt("This is my Password", salt="SomeSalt"))'

The result will be $1$SomeSalt$UqddPX3r4kH3UL5jq5/ZI., as in Mxx' answer.

Better still, use SHA512 instead of MD5:

python3 -c 'from passlib.hash import sha512_crypt; \
      print(sha512_crypt.encrypt("This is my Password", salt="SomeSalt"))' 

Result:

>$6$rounds=656000$SomeSalt$oYpmnpZahIsvn5FK8g4bDFEAmGpEN114Fe6Ko4HvinzFaz5Rq2UXQxoJZ9ZQyQoi9zaBo3gBH/FEAov3FHv48

Solution 19 - Bash

I have created an ansible-playbook that allows you to create a linux account that allows password authentication.

See AnsibleLinuxAccountCreator.

The hashed password is generated using mkpasswd command. I've provided the ways to install mkpasswd on different operating systems.

Here are the steps required to use my script:

  1. Replace <your_user_name> and <your_password> inside run.sh with your desired user name and password.

  2. Change the connection information in inventory so that ansible can connect to the machine to create a user.

  3. Run ./run.sh to execute the script.

Solution 20 - Bash

If you'd like to accomplish this as a Ansible ad-hoc command you can do the following:

$ password='SomethingSecret!'
$ ansible 192.168.1.10 -i some_inventory -b -m user -a "name=joe_user \
       update_password=always password=\"{{ \"$password\" | password_hash('sha512') }}\""

Output from above command:

192.168.1.10 | SUCCESS => {
    "append": false,
    "changed": true,
    "comment": "Joe User",
    "group": 999,
    "home": "/home/joe_user",
    "move_home": false,
    "name": "joe_user",
    "password": "NOT_LOGGING_PASSWORD",
    "shell": "/bin/bash",
    "state": "present",
    "uid": 999
}

Solution 21 - Bash

I know that I'm late to the party, but there is another solution that I'm using. It might be handy for distros that don't have --stdin in passwd binary.

- hosts: localhost
  become: True
  tasks:
    - name: Change user password
      shell: "yes '{{ item.pass }}' | passwd {{ item.user }}"
      loop:
       - { pass: 123123, user: foo }
       - { pass: asdf, user: bar }
      loop_control:
        label: "{{ item.user }}"

Label in loop_control is responsible for printing only username. The whole playbook or just user variables (you can use vars_files:) should be encrypted with ansible-vault.

Solution 22 - Bash

I couldn't get any answers here to work though with vaulted variables. So, for anyone who is using "vaulted" passwords and getting this error:

"msg": "Unexpected templating type error occurred on ({{ jinja template stuff }}): expected string or bytes-like object"

You need to pipe '|' the vault encrypted variable through "string" to get it to work.

- name: Creating user "{{ uusername }}" with admin access
  user: 
    name: {{ uusername }}
    password: "{{ upassword | string | password_hash('sha512', (upassword_salt | string)) }}"
    groups: admin append=yes
  when:  assigned_role  == "yes"

Based on Ansible solution for GitHub issue #24425

Building off code from an answer by thinkingmonster and comment by michael-aicher.

Solution 23 - Bash

Well I'am totally late to party :) I had the need for ansible play that creates multiple local users with randoms passwords. This what I came up with, used some of examples from top and put them together with some changes.

create-user-with-password.yml

---
# create_user playbook

- hosts: all
  become: True
  user: root
  vars:
#Create following user
   users:
    - test24
    - test25
#with group
   group: wheel
  roles:
    - create-user-with-password

/roles/create-user-with-password/tasks/main.yml

- name: Generate password for new user
  local_action: shell pwgen -s -N 1 20
  register: user_password
  with_items: "{{ users }}"
  run_once: true

- name: Generate encrypted password
  local_action: shell python -c 'import crypt; print(crypt.crypt( "{{ item.stdout }}", crypt.mksalt(crypt.METHOD_SHA512)))'
  register: encrypted_user_password
  with_items: "{{ user_password.results }}"
  run_once: true

- name: Create new user with group
  user:
    name: "{{ item }}"
    groups: "{{ group }}"
    shell: /bin/bash
    append: yes
    createhome: yes
    comment: 'Created with ansible'
  with_items:
    - "{{ users }}"
  register: user_created

- name: Update user Passwords
  user:
    name: '{{ item.0 }}'
    password: '{{ item.1.stdout }}'
  with_together:
    - "{{ users }}"
    - "{{ encrypted_user_password.results }}"
  when: user_created.changed

- name: Force user to change the password at first login
  shell: chage -d 0 "{{ item }}"
  with_items:
    - "{{ users }}"
  when: user_created.changed

- name: Save Passwords Locally
  become: no
  local_action: copy content={{ item.stdout }} dest=./{{ item.item }}.txt
  with_items: "{{ user_password.results }}"
  when: user_created.changed

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
Questionraphael_turtleView Question on Stackoverflow
Solution 1 - BashthinkingmonsterView Answer on Stackoverflow
Solution 2 - BashMxxView Answer on Stackoverflow
Solution 3 - BashmadheadView Answer on Stackoverflow
Solution 4 - BashArtem FeofanovView Answer on Stackoverflow
Solution 5 - BashbbaassssiieeView Answer on Stackoverflow
Solution 6 - BashmckelvinView Answer on Stackoverflow
Solution 7 - BashDjangoChainedView Answer on Stackoverflow
Solution 8 - Bashalex naumovView Answer on Stackoverflow
Solution 9 - BashhecbumaView Answer on Stackoverflow
Solution 10 - BashLightmedView Answer on Stackoverflow
Solution 11 - BashpgarefView Answer on Stackoverflow
Solution 12 - BashApuri SrikanthView Answer on Stackoverflow
Solution 13 - BashChristian BerendtView Answer on Stackoverflow
Solution 14 - BashDmytroView Answer on Stackoverflow
Solution 15 - BashSteve MidgleyView Answer on Stackoverflow
Solution 16 - BashpmoksuzView Answer on Stackoverflow
Solution 17 - BashleldoView Answer on Stackoverflow
Solution 18 - BashtexnicView Answer on Stackoverflow
Solution 19 - BashBrianView Answer on Stackoverflow
Solution 20 - BashslmView Answer on Stackoverflow
Solution 21 - BashAlex BaranowskiView Answer on Stackoverflow
Solution 22 - BashCharm000View Answer on Stackoverflow
Solution 23 - BashJuhani View Answer on Stackoverflow