Repository: ansible/ansible

Issue:
password plugin generates inappropriate default passwords
##### ISSUE TYPE
 - Bug

##### COMPONENT NAME
lib/ansible/plugins/lookup/password.py

##### ANSIBLE VERSION
```
ansible 2.3.2.0
  config file =
  configured module search path = Default w/o overrides
  python version = 2.7.13 (default, Jan 19 2017, 14:48:08) [GCC 6.3.0 20170118]
```
The dev version is also affected.

##### CONFIGURATION
ansible.cfg
```
[defaults]
host_key_checking               = no
allow_world_readable_tmpfiles   = yes
force_handlers                  = yes
squash_actions                  = yes

[ssh_connection]
ssh_args                        = -o ControlMaster=auto -o ControlPersist=10m -o UserKnownHostsFile=/dev/null -o IdentitiesOnly=yes
control_path                    = ~/.ssh/ansible-%%r@%%h:%%p
```
inventory
```
deploy-vm ansible_connection=local
development-vm ansible_ssh_host=192.168.100.100 ansible_ssh_private_key_file=/vagrant/.vagrant/machines/development-vm/virtualbox/private_key ansible_user=vagrant ansible_become=true
```

##### OS / ENVIRONMENT
VM with Debian Stretch created with Vagrant using Ansible as provisioner:
https://github.com/FibreFoX/vagrant-ansible-multi-vm

##### SUMMARY
The password plugin sometimes generates a password containing a colon. It seems that the user module invokes the command "useradd" to create users which does not get along with such passwords.
Somehow it just works with colons, if I hash the password in the task with sha512 
`| password_hash('sha512')`
It also works, if I add an user manually using the commands "useradd" and "passwd":
$ sudo useradd nom
$ sudo passwd nom
Enter new UNIX password: some:password
Retype new UNIX password: some:password
passwd: password updated successfully

There is no documentation that colons are not allowed.

##### STEPS TO REPRODUCE
manually: sudo useradd someuser -p "some:password"

Repository to reproduce "ansible-issue-28772": https://github.com/keks24/ansible-issue-28772

create_user_and_group.yml
```yaml
- name: create user "{{ username }}", add to group "{{ username }}", set a random password with 20 random characters, set user shell to "/bin/bash" and set home directory to "/var/www/{{ domain_name }}/"
  user:
    name: "{{ username }}"
    group: "{{ username }}"
    # the comment lines work, the other one not
    #password: "{{ lookup('password', '/home/vagrant/credentials/password_{{ username }} chars=ascii_letters,digits,hexdigits') | password_hash('sha512') }}"
    #password: "{{ lookup('password', '/home/vagrant/credentials/password_{{ username }}') | password_hash('sha512') }}"
    password: "{{ lookup('password', '/home/vagrant/credentials/password_{{ username }}') }}"
    shell: "/bin/bash"
    home: "/var/www/{{ domain_name }}/"
    state: present
    update_password: on_create
```

##### EXPECTED RESULTS
```
TASK [apache2 : create user "user", add to group "user", set a random password with 20 random characters, set user shell to "/bin/bash" and set home directory to "/var/www/somedomainname"] ***
changed: [development-vm]
```

##### ACTUAL RESULTS
```
TASK [apache2 : create user "user", add to group "user", set a random password with 20 random characters, set user shell to "/bin/bash" and set home directory to "/var/www/somedomainname"] ***
task path: /vagrant/provisioning/roles/apache2/tasks/create_user_and_group.yml:12
Using module file /usr/local/lib/python2.7/dist-packages/ansible/modules/system/user.py
<192.168.100.100> ESTABLISH SSH CONNECTION FOR USER: vagrant
<192.168.100.100> SSH: EXEC ssh -vvv -o ControlMaster=auto -o ControlPersist=10m -o UserKnownHostsFile=/dev/null -o IdentitiesOnly=yes -o StrictHostKeyChecking=no -o 'IdentityFile="/vagrant/.vagrant/machines/development-vm/virtualbox/private_key"' -o KbdInteractiveAuthentication=no -o PreferredAuthentications=gssapi-with-mic,gssapi-keyex,hostbased,publickey -o PasswordAuthentication=no -o User=vagrant -o ConnectTimeout=10 -o 'ControlPath=~/.ssh/ansible-%r@%h:%p' 192.168.100.100 '/bin/sh -c '"'"'echo ~ && sleep 0'"'"''                                                                                                                                                                                            
<192.168.100.100> (0, '/home/vagrant\n', 'OpenSSH_7.4p1 Debian-10+deb9u1, OpenSSL 1.0.2l  25 May 2017\r\ndebug1: Reading configuration data /etc/ssh/ssh_config\r\ndebug1: /etc/ssh/ssh_config line 19: Applying options for *\r\ndebug1: auto-mux: Trying existing master\r\ndebug2: fd 3 setting O_NONBLOCK\r\ndebug2: mux_client_hello_exchange: master version 4\r\ndebug3: mux_client_forwards: request forwardings: 0 local, 0 remote\r\ndebug3: mux_client_request_session: entering\r\ndebug3: mux_client_request_alive: entering\r\ndebug3: mux_client_request_alive: done pid = 13414\r\ndebug3: mux_client_request_session: session request sent\r\ndebug1: mux_client_request_session: master session id: 2\r\ndebug3: mux_client_read_packet: read header failed: Broken pipe\r\ndebug2: Received exit status from master 0\r\n')                                                                                                                                      
<192.168.100.100> ESTABLISH SSH CONNECTION FOR USER: vagrant
<192.168.100.100> SSH: EXEC ssh -vvv -o ControlMaster=auto -o ControlPersist=10m -o UserKnownHostsFile=/dev/null -o IdentitiesOnly=yes -o StrictHostKeyChecking=no -o 'IdentityFile="/vagrant/.vagrant/machines/development-vm/virtualbox/private_key"' -o KbdInteractiveAuthentication=no -o PreferredAuthentications=gssapi-with-mic,gssapi-keyex,hostbased,publickey -o PasswordAuthentication=no -o User=vagrant -o ConnectTimeout=10 -o 'ControlPath=~/.ssh/ansible-%r@%h:%p' 192.168.100.100 '/bin/sh -c '"'"'( umask 77 && mkdir -p "` echo /home/vagrant/.ansible/tmp/ansible-tmp-1504015228.12-146585718646730 `" && echo ansible-tmp-1504015228.12-146585718646730="` echo /home/vagrant/.ansible/tmp/ansible-tmp-1504015228.12-146585718646730 `" ) && sleep 0'"'"''                                                                                                                                                                                                     
<192.168.100.100> (0, 'ansible-tmp-1504015228.12-146585718646730=/home/vagrant/.ansible/tmp/ansible-tmp-1504015228.12-146585718646730\n', 'OpenSSH_7.4p1 Debian-10+deb9u1, OpenSSL 1.0.2l  25 May 2017\r\ndebug1: Reading configuration data /etc/ssh/ssh_config\r\ndebug1: /etc/ssh/ssh_config line 19: Applying options for *\r\ndebug1: auto-mux: Trying existing master\r\ndebug2: fd 3 setting O_NONBLOCK\r\ndebug2: mux_client_hello_exchange: master version 4\r\ndebug3: mux_client_forwards: request forwardings: 0 local, 0 remote\r\ndebug3: mux_client_request_session: entering\r\ndebug3: mux_client_request_alive: entering\r\ndebug3: mux_client_request_alive: done pid = 13414\r\ndebug3: mux_client_request_session: session request sent\r\ndebug1: mux_client_request_session: master session id: 2\r\ndebug3: mux_client_read_packet: read header failed: Broken pipe\r\ndebug2: Received exit status from master 0\r\n')                                     
<192.168.100.100> PUT /tmp/tmpxZSEKM TO /home/vagrant/.ansible/tmp/ansible-tmp-1504015228.12-146585718646730/user.py
<192.168.100.100> SSH: EXEC sftp -b - -vvv -o ControlMaster=auto -o ControlPersist=10m -o UserKnownHostsFile=/dev/null -o IdentitiesOnly=yes -o StrictHostKeyChecking=no -o 'IdentityFile="/vagrant/.vagrant/machines/development-vm/virtualbox/private_key"' -o KbdInteractiveAuthentication=no -o PreferredAuthentications=gssapi-with-mic,gssapi-keyex,hostbased,publickey -o PasswordAuthentication=no -o User=vagrant -o ConnectTimeout=10 -o 'ControlPath=~/.ssh/ansible-%r@%h:%p' '[192.168.100.100]'                                                                                                                                                                                                                           
<192.168.100.100> (0, 'sftp> put /tmp/tmpxZSEKM /home/vagrant/.ansible/tmp/ansible-tmp-1504015228.12-146585718646730/user.py\n', 'OpenSSH_7.4p1 Debian-10+deb9u1, OpenSSL 1.0.2l  25 May 2017\r\ndebug1: Reading configuration data /etc/ssh/ssh_config\r\ndebug1: /etc/ssh/ssh_config line 19: Applying options for *\r\ndebug1: auto-mux: Trying existing master\r\ndebug2: fd 3 setting O_NONBLOCK\r\ndebug2: mux_client_hello_exchange: master version 4\r\ndebug3: mux_client_forwards: request forwardings: 0 local, 0 remote\r\ndebug3: mux_client_request_session: entering\r\ndebug3: mux_client_request_alive: entering\r\ndebug3: mux_client_request_alive: done pid = 13414\r\ndebug3: mux_client_request_session: session request sent\r\ndebug1: mux_client_request_session: master session id: 2\r\ndebug2: Remote version: 3\r\ndebug2: Server supports extension "posix-rename@openssh.com" revision 1\r\ndebug2: Server supports extension "statvfs@openssh.com" revision 2\r\ndebug2: Server supports extension "fstatvfs@openssh.com" revision 2\r\ndebug2: Server supports extension "hardlink@openssh.com" revision 1\r\ndebug2: Server supports extension "fsync@openssh.com" revision 1\r\ndebug3: Sent message fd 5 T:16 I:1\r\ndebug3: SSH_FXP_REALPATH . -> /home/vagrant size 0\r\ndebug3: Looking up /tmp/tmpxZSEKM\r\ndebug3: Sent message fd 5 T:17 I:2\r\ndebug3: Received stat reply T:101 I:2\r\ndebug1: Couldn\'t stat remote file: No such file or directory\r\ndebug3: Sent message SSH2_FXP_OPEN I:3 P:/home/vagrant/.ansible/tmp/ansible-tmp-1504015228.12-146585718646730/user.py\r\ndebug3: Sent message SSH2_FXP_WRITE I:4 O:0 S:32768\r\ndebug3: SSH2_FXP_STATUS 0\r\ndebug3: In write loop, ack for 4 32768 bytes at 0\r\ndebug3: Sent message SSH2_FXP_WRITE I:5 O:32768 S:32768\r\ndebug3: Sent message SSH2_FXP_WRITE I:6 O:65536 S:5872\r\ndebug3: SSH2_FXP_STATUS 0\r\ndebug3: In write loop, ack for 5 32768 bytes at 32768\r\ndebug3: SSH2_FXP_STATUS 0\r\ndebug3: In write loop, ack for 6 5872 bytes at 65536\r\ndebug3: Sent message SSH2_FXP_CLOSE I:4\r\ndebug3: SSH2_FXP_STATUS 0\r\ndebug3: mux_client_read_packet: read header failed: Broken pipe\r\ndebug2: Received exit status from master 0\r\n')                                                                                                                                                                                                 
<192.168.100.100> ESTABLISH SSH CONNECTION FOR USER: vagrant
<192.168.100.100> SSH: EXEC ssh -vvv -o ControlMaster=auto -o ControlPersist=10m -o UserKnownHostsFile=/dev/null -o IdentitiesOnly=yes -o StrictHostKeyChecking=no -o 'IdentityFile="/vagrant/.vagrant/machines/development-vm/virtualbox/private_key"' -o KbdInteractiveAuthentication=no -o PreferredAuthentications=gssapi-with-mic,gssapi-keyex,hostbased,publickey -o PasswordAuthentication=no -o User=vagrant -o ConnectTimeout=10 -o 'ControlPath=~/.ssh/ansible-%r@%h:%p' 192.168.100.100 '/bin/sh -c '"'"'chmod u+x /home/vagrant/.ansible/tmp/ansible-tmp-1504015228.12-146585718646730/ /home/vagrant/.ansible/tmp/ansible-tmp-1504015228.12-146585718646730/user.py && sleep 0'"'"''                                      
<192.168.100.100> (0, '', 'OpenSSH_7.4p1 Debian-10+deb9u1, OpenSSL 1.0.2l  25 May 2017\r\ndebug1: Reading configuration data /etc/ssh/ssh_config\r\ndebug1: /etc/ssh/ssh_config line 19: Applying options for *\r\ndebug1: auto-mux: Trying existing master\r\ndebug2: fd 3 setting O_NONBLOCK\r\ndebug2: mux_client_hello_exchange: master version 4\r\ndebug3: mux_client_forwards: request forwardings: 0 local, 0 remote\r\ndebug3: mux_client_request_session: entering\r\ndebug3: mux_client_request_alive: entering\r\ndebug3: mux_client_request_alive: done pid = 13414\r\ndebug3: mux_client_request_session: session request sent\r\ndebug1: mux_client_request_session: master session id: 2\r\ndebug3: mux_client_read_packet: read header failed: Broken pipe\r\ndebug2: Received exit status from master 0\r\n')                                                                                                                                                     
<192.168.100.100> ESTABLISH SSH CONNECTION FOR USER: vagrant
<192.168.100.100> SSH: EXEC ssh -vvv -o ControlMaster=auto -o ControlPersist=10m -o UserKnownHostsFile=/dev/null -o IdentitiesOnly=yes -o StrictHostKeyChecking=no -o 'IdentityFile="/vagrant/.vagrant/machines/development-vm/virtualbox/private_key"' -o KbdInteractiveAuthentication=no -o PreferredAuthentications=gssapi-with-mic,gssapi-keyex,hostbased,publickey -o PasswordAuthentication=no -o User=vagrant -o ConnectTimeout=10 -o 'ControlPath=~/.ssh/ansible-%r@%h:%p' -tt 192.168.100.100 '/bin/sh -c '"'"'sudo -H -S -n -u root /bin/sh -c '"'"'"'"'"'"'"'"'echo BECOME-SUCCESS-eiwtluqxvbadzelgyrptaeuohidsqcul; /usr/bin/python /home/vagrant/.ansible/tmp/ansible-tmp-1504015228.12-146585718646730/user.py; rm -rf "/home/vagrant/.ansible/tmp/ansible-tmp-1504015228.12-146585718646730/" > /dev/null 2>&1'"'"'"'"'"'"'"'"' && sleep 0'"'"''
<192.168.100.100> (0, '\r\n{"msg": "useradd: invalid field \'********\'\\n", "failed": true, "rc": 3, "name": "user", "invocation": {"module_args": {"comment": null, "ssh_key_bits": 0, "update_password": "on_create", "non_unique": false, "force": false, "ssh_key_type": "rsa", "ssh_key_passphrase": null, "createhome": true, "uid": null, "home": "/var/www/somedomainname", "append": false, "skeleton": null, "ssh_key_comment": "ansible-generated on hostname", "group": "user", "system": false, "state": "present", "shell": "/bin/bash", "expires": null, "ssh_key_file": null, "groups": null, "move_home": false, "password": "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER", "name": "user", "seuser": null, "remove": false, "login_class": null, "generate_ssh_key": null}}}\r\n', 'OpenSSH_7.4p1 Debian-10+deb9u1, OpenSSL 1.0.2l  25 May 2017\r\ndebug1: Reading configuration data /etc/ssh/ssh_config\r\ndebug1: /etc/ssh/ssh_config line 19: Applying options for *\r\ndebug1: auto-mux: Trying existing master\r\ndebug2: fd 3 setting O_NONBLOCK\r\ndebug2: mux_client_hello_exchange: master version 4\r\ndebug3: mux_client_forwards: request forwardings: 0 local, 0 remote\r\ndebug3: mux_client_request_session: entering\r\ndebug3: mux_client_request_alive: entering\r\ndebug3: mux_client_request_alive: done pid = 13414\r\ndebug3: mux_client_request_session: session request sent\r\ndebug1: mux_client_request_session: master session id: 2\r\ndebug3: mux_client_read_packet: read header failed: Broken pipe\r\ndebug2: Received exit status from master 0\r\nShared connection to 192.168.100.100 closed.\r\n')
fatal: [development-vm]: FAILED! => {
    "changed": false,
    "failed": true,
    "invocation": {
        "module_args": {
            "append": false,
            "comment": null,
            "createhome": true,
            "expires": null,
            "force": false,
            "generate_ssh_key": null,
            "group": "user",
            "groups": null,
            "home": "/var/www/somedomainname",
            "login_class": null,
            "move_home": false,
            "name": "user",
            "non_unique": false,
            "password": "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER",
            "remove": false,
            "seuser": null,
            "shell": "/bin/bash",
            "skeleton": null,
            "ssh_key_bits": 0,
            "ssh_key_comment": "ansible-generated on hostname",
            "ssh_key_file": null,
            "ssh_key_passphrase": null,
            "ssh_key_type": "rsa",
            "state": "present",
            "system": false,
            "uid": null,
            "update_password": "on_create"
        }
    },
    "msg": "useradd: invalid field '********'\n",
    "name": "user",
    "rc": 3
}
        to retry, use: --limit @/vagrant/provisioning/create_structure.retry

PLAY RECAP *********************************************************************
development-vm             : ok=23   changed=1    unreachable=0    failed=1

Ansible failed to complete successfully. Any error output should be
visible above. Please fix these errors and try again.
```

Vagrantfile
```ruby
# -*- mode: ruby -*-
# vi: set ft=ruby :

Vagrant.configure("2") do |config|
    config.vm.box = "debian/stretch64"

    ## when using non "contrib-" boxes
    ## do NOT use rsync: https://stackoverflow.com/a/34882657/1961102
    ## and use special mount-options, otherwise SSH fails due to unprotected SSH private key: https://stackoverflow.com/a/34485973/1961102
    config.vm.synced_folder ".", "/vagrant", type: "virtualbox", owner: "vagrant", mount_options: ["dmode=775,fmode=600"]

    # disable auto_update on "debian/contrib-jessie64" box, as it is not working
    if Vagrant.has_plugin?("vagrant-vbguest")
        config.vbguest.auto_update = true
        # never download from online, always use local installed one
        config.vbguest.no_remote = true
    end

    config.vm.box_check_update = true

    # internal VM which is configured like the real machine
    config.vm.define "development-vm" do |vmConfig|
        # do NOT have shared vagrant-folder present (as this requires rsync or guest-additions to be installed)
        vmConfig.vm.synced_folder ".", "/vagrant", disabled: true
        # do NOT install guest-additions
        vmConfig.vbguest.auto_update = false

        vmConfig.vm.network "private_network", ip: "192.168.100.100", virtualbox__intnet: "development_network"
        vmConfig.vm.network "forwarded_port", guest: 80, host: 24242

        vmConfig.vm.provider "virtualbox" do |vb|
            vb.memory = "4096"
            vb.cpus = 2
            vb.name = "development-vm"

            # make it small ;)
            vb.linked_clone = true
        end
    end

    # internal VM to deploy from
    config.vm.define "provision-vm" do |vmConfig|
        config.vm.hostname = "provision-vm"
        vmConfig.vm.network "private_network", ip: "192.168.100.99", virtualbox__intnet: "development_network"

        vmConfig.vm.provider "virtualbox" do |vb|
            # we dont need much for this VM
            vb.memory = "512"
            vb.cpus = 1
            vb.name = "provision-vm"

            # make it small ;)
            vb.linked_clone = true
        end

        # as I'm developing on Windows, there is no local ansible, so use ansible (via PIP) inside the VM
        vmConfig.vm.provision "ansible_local" do |ansible|
            ansible.playbook = "provisioning/create_structure.yml"
            # special inventory file, since we have two VMs here
            ansible.inventory_path = "provisioning/inventory"
            # required for disabling "unknown hosts"-check
            ansible.config_file = "provisioning/ansible.cfg"
            ansible.verbose = false
            ansible.limit = "development-vm"
            ansible.install_mode = :pip
        end

        if ENV['playbook'] == "deploy"
            vmConfig.vm.provision "ansible_local" do |ansible|
                ansible.playbook = "provisioning/deploy_files.yml"
                # special inventory file, since we have two VMs here
                ansible.inventory_path = "provisioning/inventory"
                # required for disabling "unknown hosts"-check
                ansible.config_file = "provisioning/ansible.cfg"
                ansible.verbose = false
                ansible.limit = "development-vm"
                ansible.install_mode = :pip
            end
        end
    end
end
```



Discussion:
Hi, 

I'm running into the same problem, but I'm invoking my password field a bit differently:

**ISSUE TYPE**
Bug

**COMPONENT NAME**
(None)

**ANSIBLE VERSION**
ansible 2.1.1.0
  config file = /etc/ansible/ansible.cfg
  configured module search path = Default w/o overrides
Python version = 2.7.12

**OS Environment**
VM with Ubuntu Xenial

**Summary**
When invoking the following task in the user module, I receive a failed error on 3 test hosts running the same version of Ubuntu and Python. If I run the same playbook again on the same 5 hosts, a different behavior will occur where I will receive a failed error on 2 hosts. Subsequent runs after would default back to the 3 test hosts experiencing errors.

Steps to reproduce:

new_user.yml

```
---
- hosts: all

  become: yes

  vars_prompt:
   - name: "new_user"
     prompt: "What is the name of the new user to create and setup?"
     private: no

  tasks:

   - name: Create new user account
     when:
       - new_user != "root"
     user:
       name: "{{ new_user }}"
       shell: /bin/bash
       createhome: yes
       password: "{{lookup('pipe', 'pwgen -N 1 -s -y 80') }}"

```

**Results**

> task path: /opt/ansible/setup_scripts/create_user.yml:21
<192.168.1.11> ESTABLISH SSH CONNECTION FOR USER: default_user
<192.168.1.14> ESTABLISH SSH CONNECTION FOR USER: default_user
<192.168.1.13> ESTABLISH SSH CONNECTION FOR USER: default_user
<192.168.1.15> ESTABLISH SSH CONNECTION FOR USER: default_user
<192.168.1.16> ESTABLISH SSH CONNECTION FOR USER: default_user
<192.168.1.14> SSH: EXEC sshpass -d24 ssh -C -q -o ControlMaster=auto -o ControlPersist=60s -o Port=22 -o User=default_user -o ConnectTimeout=10 -o ControlPath=/root/.ansible/cp/ansible-ssh-%h-%p-%r 192.168.1.14 '/bin/sh -c '"'"'( umask 77 && mkdir -p "` echo $HOME/.ansible/tmp/ansible-tmp-1506608999.84-27450073604495 `" && echo ansible-tmp-1506608999.84-27450073604495="` echo $HOME/.ansible/tmp/ansible-tmp-1506608999.84-27450073604495 `" ) && sleep 0'"'"''
<192.168.1.15> SSH: EXEC sshpass -d24 ssh -C -q -o ControlMaster=auto -o ControlPersist=60s -o Port=22 -o User=default_user -o ConnectTimeout=10 -o ControlPath=/root/.ansible/cp/ansible-ssh-%h-%p-%r 192.168.1.15 '/bin/sh -c '"'"'( umask 77 && mkdir -p "` echo $HOME/.ansible/tmp/ansible-tmp-1506608999.84-64970704224903 `" && echo ansible-tmp-1506608999.84-64970704224903="` echo $HOME/.ansible/tmp/ansible-tmp-1506608999.84-64970704224903 `" ) && sleep 0'"'"''
<192.168.1.13> SSH: EXEC sshpass -d24 ssh -C -q -o ControlMaster=auto -o ControlPersist=60s -o Port=22 -o User=default_user -o ConnectTimeout=10 -o ControlPath=/root/.ansible/cp/ansible-ssh-%h-%p-%r 192.168.1.13 '/bin/sh -c '"'"'( umask 77 && mkdir -p "` echo $HOME/.ansible/tmp/ansible-tmp-1506608999.84-19561008999370 `" && echo ansible-tmp-1506608999.84-19561008999370="` echo $HOME/.ansible/tmp/ansible-tmp-1506608999.84-19561008999370 `" ) && sleep 0'"'"''
<192.168.1.16> SSH: EXEC sshpass -d24 ssh -C -q -o ControlMaster=auto -o ControlPersist=60s -o Port=22 -o User=default_user -o ConnectTimeout=10 -o ControlPath=/root/.ansible/cp/ansible-ssh-%h-%p-%r 192.168.1.16 '/bin/sh -c '"'"'( umask 77 && mkdir -p "` echo $HOME/.ansible/tmp/ansible-tmp-1506608999.84-275055177175071 `" && echo ansible-tmp-1506608999.84-275055177175071="` echo $HOME/.ansible/tmp/ansible-tmp-1506608999.84-275055177175071 `" ) && sleep 0'"'"''
<192.168.1.11> SSH: EXEC sshpass -d24 ssh -C -q -o ControlMaster=auto -o ControlPersist=60s -o Port=22 -o User=default_user -o ConnectTimeout=10 -o ControlPath=/root/.ansible/cp/ansible-ssh-%h-%p-%r 192.168.1.11 '/bin/sh -c '"'"'( umask 77 && mkdir -p "` echo $HOME/.ansible/tmp/ansible-tmp-1506608999.84-22688867209285 `" && echo ansible-tmp-1506608999.84-22688867209285="` echo $HOME/.ansible/tmp/ansible-tmp-1506608999.84-22688867209285 `" ) && sleep 0'"'"''
<192.168.1.15> PUT /tmp/tmpKpgTPX TO /home/default_user/.ansible/tmp/ansible-tmp-1506608999.84-64970704224903/user
<192.168.1.15> SSH: EXEC sshpass -d24 sftp -o BatchMode=no -b - -C -o ControlMaster=auto -o ControlPersist=60s -o Port=22 -o User=default_user -o ConnectTimeout=10 -o ControlPath=/root/.ansible/cp/ansible-ssh-%h-%p-%r '[192.168.1.15]'
<192.168.1.14> PUT /tmp/tmpc4hcWq TO /home/default_user/.ansible/tmp/ansible-tmp-1506608999.84-27450073604495/user
<192.168.1.14> SSH: EXEC sshpass -d24 sftp -o BatchMode=no -b - -C -o ControlMaster=auto -o ControlPersist=60s -o Port=22 -o User=default_user -o ConnectTimeout=10 -o ControlPath=/root/.ansible/cp/ansible-ssh-%h-%p-%r '[192.168.1.14]'
<192.168.1.13> PUT /tmp/tmpHb6OCv TO /home/default_user/.ansible/tmp/ansible-tmp-1506608999.84-19561008999370/user
<192.168.1.16> PUT /tmp/tmpHkZOq9 TO /home/default_user/.ansible/tmp/ansible-tmp-1506608999.84-275055177175071/user
<192.168.1.16> SSH: EXEC sshpass -d24 sftp -o BatchMode=no -b - -C -o ControlMaster=auto -o ControlPersist=60s -o Port=22 -o User=default_user -o ConnectTimeout=10 -o ControlPath=/root/.ansible/cp/ansible-ssh-%h-%p-%r '[192.168.1.16]'
<192.168.1.11> PUT /tmp/tmp5EY3XJ TO /home/default_user/.ansible/tmp/ansible-tmp-1506608999.84-22688867209285/user
<192.168.1.11> SSH: EXEC sshpass -d24 sftp -o BatchMode=no -b - -C -o ControlMaster=auto -o ControlPersist=60s -o Port=22 -o User=default_user -o ConnectTimeout=10 -o ControlPath=/root/.ansible/cp/ansible-ssh-%h-%p-%r '[192.168.1.11]'
<192.168.1.13> SSH: EXEC sshpass -d24 sftp -o BatchMode=no -b - -C -o ControlMaster=auto -o ControlPersist=60s -o Port=22 -o User=default_user -o ConnectTimeout=10 -o ControlPath=/root/.ansible/cp/ansible-ssh-%h-%p-%r '[192.168.1.13]'
<192.168.1.14> ESTABLISH SSH CONNECTION FOR USER: default_user
<192.168.1.14> SSH: EXEC sshpass -d24 ssh -C -q -o ControlMaster=auto -o ControlPersist=60s -o Port=22 -o User=default_user -o ConnectTimeout=10 -o ControlPath=/root/.ansible/cp/ansible-ssh-%h-%p-%r -tt 192.168.1.14 '/bin/sh -c '"'"'sudo -H -S  -p "[sudo via ansible, key=urpcjeprthwzxyvzuoazpupkvmqjxqbj] password: " -u root /bin/sh -c '"'"'"'"'"'"'"'"'echo BECOME-SUCCESS-urpcjeprthwzxyvzuoazpupkvmqjxqbj; LANG=en_US.UTF-8 LC_ALL=en_US.UTF-8 LC_MESSAGES=en_US.UTF-8 /usr/bin/python /home/default_user/.ansible/tmp/ansible-tmp-1506608999.84-27450073604495/user; rm -rf "/home/default_user/.ansible/tmp/ansible-tmp-1506608999.84-27450073604495/" > /dev/null 2>&1'"'"'"'"'"'"'"'"' && sleep 0'"'"''
<192.168.1.13> ESTABLISH SSH CONNECTION FOR USER: default_user
<192.168.1.13> SSH: EXEC sshpass -d24 ssh -C -q -o ControlMaster=auto -o ControlPersist=60s -o Port=22 -o User=default_user -o ConnectTimeout=10 -o ControlPath=/root/.ansible/cp/ansible-ssh-%h-%p-%r -tt 192.168.1.13 '/bin/sh -c '"'"'sudo -H -S  -p "[sudo via ansible, key=onvsedpqfecafzymdcocijmswxmcihov] password: " -u root /bin/sh -c '"'"'"'"'"'"'"'"'echo BECOME-SUCCESS-onvsedpqfecafzymdcocijmswxmcihov; LANG=en_US.UTF-8 LC_ALL=en_US.UTF-8 LC_MESSAGES=en_US.UTF-8 /usr/bin/python /home/default_user/.ansible/tmp/ansible-tmp-1506608999.84-19561008999370/user; rm -rf "/home/default_user/.ansible/tmp/ansible-tmp-1506608999.84-19561008999370/" > /dev/null 2>&1'"'"'"'"'"'"'"'"' && sleep 0'"'"''
<192.168.1.16> ESTABLISH SSH CONNECTION FOR USER: default_user
<192.168.1.16> SSH: EXEC sshpass -d24 ssh -C -q -o ControlMaster=auto -o ControlPersist=60s -o Port=22 -o User=default_user -o ConnectTimeout=10 -o ControlPath=/root/.ansible/cp/ansible-ssh-%h-%p-%r -tt 192.168.1.16 '/bin/sh -c '"'"'sudo -H -S  -p "[sudo via ansible, key=cboqncunsvuzvubseyaeheeboddgrjkr] password: " -u root /bin/sh -c '"'"'"'"'"'"'"'"'echo BECOME-SUCCESS-cboqncunsvuzvubseyaeheeboddgrjkr; LANG=en_US.UTF-8 LC_ALL=en_US.UTF-8 LC_MESSAGES=en_US.UTF-8 /usr/bin/python /home/default_user/.ansible/tmp/ansible-tmp-1506608999.84-275055177175071/user; rm -rf "/home/default_user/.ansible/tmp/ansible-tmp-1506608999.84-275055177175071/" > /dev/null 2>&1'"'"'"'"'"'"'"'"' && sleep 0'"'"''
<192.168.1.15> ESTABLISH SSH CONNECTION FOR USER: default_user
<192.168.1.15> SSH: EXEC sshpass -d24 ssh -C -q -o ControlMaster=auto -o ControlPersist=60s -o Port=22 -o User=default_user -o ConnectTimeout=10 -o ControlPath=/root/.ansible/cp/ansible-ssh-%h-%p-%r -tt 192.168.1.15 '/bin/sh -c '"'"'sudo -H -S  -p "[sudo via ansible, key=kygnmnabzyumgntdenkbfowabssnuonb] password: " -u root /bin/sh -c '"'"'"'"'"'"'"'"'echo BECOME-SUCCESS-kygnmnabzyumgntdenkbfowabssnuonb; LANG=en_US.UTF-8 LC_ALL=en_US.UTF-8 LC_MESSAGES=en_US.UTF-8 /usr/bin/python /home/default_user/.ansible/tmp/ansible-tmp-1506608999.84-64970704224903/user; rm -rf "/home/default_user/.ansible/tmp/ansible-tmp-1506608999.84-64970704224903/" > /dev/null 2>&1'"'"'"'"'"'"'"'"' && sleep 0'"'"''
<192.168.1.11> ESTABLISH SSH CONNECTION FOR USER: default_user
<192.168.1.11> SSH: EXEC sshpass -d24 ssh -C -q -o ControlMaster=auto -o ControlPersist=60s -o Port=22 -o User=default_user -o ConnectTimeout=10 -o ControlPath=/root/.ansible/cp/ansible-ssh-%h-%p-%r -tt 192.168.1.11 '/bin/sh -c '"'"'sudo -H -S  -p "[sudo via ansible, key=lmryswbfrbdcnlmqsovljujovdgwrpwd] password: " -u root /bin/sh -c '"'"'"'"'"'"'"'"'echo BECOME-SUCCESS-lmryswbfrbdcnlmqsovljujovdgwrpwd; LANG=en_US.UTF-8 LC_ALL=en_US.UTF-8 LC_MESSAGES=en_US.UTF-8 /usr/bin/python /home/default_user/.ansible/tmp/ansible-tmp-1506608999.84-22688867209285/user; rm -rf "/home/default_user/.ansible/tmp/ansible-tmp-1506608999.84-22688867209285/" > /dev/null 2>&1'"'"'"'"'"'"'"'"' && sleep 0'"'"''
fatal: [192.168.1.13]: FAILED! => {"changed": false, "failed": true, "invocation": {"module_args": {"append": false, "comment": null, "createhome": true, "expires": null, "force": false, "generate_ssh_key": null, "group": null, "groups": null, "home": null, "login_class": null, "move_home": false, "name": "test_user", "non_unique": false, "password": "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER", "remove": false, "seuser": null, "shell": "/bin/bash", "skeleton": null, "ssh_key_bits": "2048", "ssh_key_comment": "ansible-generated on vm-wrx-01", "ssh_key_file": null, "ssh_key_passphrase": null, "ssh_key_type": "rsa", "state": "present", "system": false, "uid": null, "update_password": "always"}, "module_name": "user"}, "msg": "useradd: invalid field '********'\n", "name": "test_user", "rc": 3}
changed: [192.168.1.14] => {"append": false, "changed": true, "comment": "", "group": 1002, "home": "/home/test_user", "invocation": {"module_args": {"append": false, "comment": null, "createhome": true, "expires": null, "force": false, "generate_ssh_key": null, "group": null, "groups": null, "home": null, "login_class": null, "move_home": false, "name": "test_user", "non_unique": false, "password": "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER", "remove": false, "seuser": null, "shell": "/bin/bash", "skeleton": null, "ssh_key_bits": "2048", "ssh_key_comment": "ansible-generated on vm-wrx-02", "ssh_key_file": null, "ssh_key_passphrase": null, "ssh_key_type": "rsa", "state": "present", "system": false, "uid": null, "update_password": "always"}, "module_name": "user"}, "move_home": false, "name": "test_user", "password": "NOT_LOGGING_PASSWORD", "shell": "/bin/bash", "state": "present", "uid": 1002}
fatal: [192.168.1.16]: FAILED! => {"changed": false, "failed": true, "invocation": {"module_args": {"append": false, "comment": null, "createhome": true, "expires": null, "force": false, "generate_ssh_key": null, "group": null, "groups": null, "home": null, "login_class": null, "move_home": false, "name": "test_user", "non_unique": false, "password": "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER", "remove": false, "seuser": null, "shell": "/bin/bash", "skeleton": null, "ssh_key_bits": "2048", "ssh_key_comment": "ansible-generated on vm-alpha-02", "ssh_key_file": null, "ssh_key_passphrase": null, "ssh_key_type": "rsa", "state": "present", "system": false, "uid": null, "update_password": "always"}, "module_name": "user"}, "msg": "usermod: failure while writing changes to /etc/shadow\n", "name": "test_user", "rc": 1}
fatal: [192.168.1.11]: FAILED! => {"changed": false, "failed": true, "invocation": {"module_args": {"append": false, "comment": null, "createhome": true, "expires": null, "force": false, "generate_ssh_key": null, "group": null, "groups": null, "home": null, "login_class": null, "move_home": false, "name": "test_user", "non_unique": false, "password": "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER", "remove": false, "seuser": null, "shell": "/bin/bash", "skeleton": null, "ssh_key_bits": "2048", "ssh_key_comment": "ansible-generated on vm-test-01", "ssh_key_file": null, "ssh_key_passphrase": null, "ssh_key_type": "rsa", "state": "present", "system": false, "uid": null, "update_password": "always"}, "module_name": "user"}, "msg": "usermod: failure while writing changes to /etc/shadow\n", "name": "test_user", "rc": 1}
changed: [192.168.1.15] => {"changed": true, "comment": "", "createhome": true, "group": 1002, "home": "/home/test_user", "invocation": {"module_args": {"append": false, "comment": null, "createhome": true, "expires": null, "force": false, "generate_ssh_key": null, "group": null, "groups": null, "home": null, "login_class": null, "move_home": false, "name": "test_user", "non_unique": false, "password": "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER", "remove": false, "seuser": null, "shell": "/bin/bash", "skeleton": null, "ssh_key_bits": "2048", "ssh_key_comment": "ansible-generated on vm-alpha-01", "ssh_key_file": null, "ssh_key_passphrase": null, "ssh_key_type": "rsa", "state": "present", "system": false, "uid": null, "update_password": "always"}, "module_name": "user"}, "name": "test_user", "password": "NOT_LOGGING_PASSWORD", "shell": "/bin/bash", "state": "present", "system": false, "uid": 1002}
So, to add a bit more mystery to the issue, it appears the 'pwgen' command still worked on the target nodes and assigned arbitrary values, but for some reason, Ansible kicked back failed messages during my initial run.
Now I understand why it is not possible to use cleartext passwords containing colons. These are used as delimiter character:
```bash
ramon@lenovo ~ # head -n 1 /etc/shadow
root:some_hashed_password_here:17299:0:::::
```
This explains why this part works because the password gets hashed before added to `/etc/shadow`:
```yml
#password: "{{ lookup('password', '/home/vagrant/credentials/password_{{ username }}') | password_hash('sha512') }}"
```
As the user-module is handing these files, I would except some checks, not just some irritating behaviour.
One further perception:
Exclamation marks and asterisks in clear text are not allowed, too since these characters are used to lock newly added users using `useradd` or `adduser`, if no password was given.
This problem is actually in ansible `user` module, the `user` module uses `useradd` command to create a new user in the system. `useradd` will try to add the user and password to the shadow file, but shadow file use `:` as the delimiter, that's why you got a `useradd: invalid field '********'\n` if there is `:` in your password.

Also please note, the man page of `useradd` says:
```
-p, --password PASSWORD
The encrypted password, as returned by crypt(3). The default is to disable the password.
```
So `useradd` expects an **encrypted (hashed)** password, a plaintext password without `:` will not trigger any invalid field error, but it's actually invalid, since the OS will interpret your plaintext password as a hashed one, and you are not able to login with that password anymore. Detail explanation is here: https://serverfault.com/questions/653203/useradd-not-encrypting-passwords-in-etc-shadow

The workaround in ansible is to hash the password first with ansible `password` lookup and then pass it to `user` module. Something like this:
```
- name: create user
  user:
    name: "some_user"
    password: "{{lookup('password', '~/Desktop/password.txt
    encrypt=sha512_crypt')}}"
    state: present
    update_password: on_create
```
Then you can find the plaintext password at `~/Desktop/password.txt'.


@HD650 thanks for the detailed explaination, that clears up a huge amount of questions we encountered back then.

Reading the current documentation of the user-module, this isn't that obvious and should be added. In addition, as a normal user, I would like to have the user-module doing some sanity-checks (as Base64 chars are known, whitelist-checks would be easy). How do you think this issue will be handled any further?
@FibreFoX Yes you are right, the error message here is not clear enough for users to solve this problem. I will add some more meaningful error message, but it's hard for ansible to do a sanity check since it's hard to say whether a string has been hashed or not, that's also why `useradd` itself can not check whether the input is hashed or not too). And sorry for the Base64 explanation, after more research it seems not accurate, different hash methods will produce different format of results, so just check the character set of input is not sufficient.
I checked the source code of [useradd.c](https://github.com/shadow-maint/shadow/blob/master/src/useradd.c#L1279), it seems it's not the problem of Base64, the code just checks is there any `:` or `\n` in the input (since `:` is used as the delimiter in the shadow file, like @keks24 said). But the basic idea is the same, we should feed `useradd` with hashed password otherwise the password will not work. I corrected my previous comment.
@HD650 So checking for these two special chars should be sufficient, am I right? (in addition to a nicer error message) Thanks for looking into this, even sneaking into that useradd-code :)
@FibreFoX yeah, I don't know any method to check whether a string is hashed or not... I will discuss with other members, maybe they have some idea.
@FibreFoX @HD650 
Do not forget `!` and `*`. These are also invalid in cleartext but are used to lock users. Hashed passwords begin with a `$` character:
```no-highlight
$<hash_algorithm>$<salt>$<hashed_password>
$1   = MD5
$2   = Blowfish
$2a  = eksblowfish
$5   = SHA-256
$6   = SHA-512
```
I do not know, if there are more.

Resolving patch:
diff --git a/lib/ansible/modules/system/user.py b/lib/ansible/modules/system/user.py
--- a/lib/ansible/modules/system/user.py
+++ b/lib/ansible/modules/system/user.py
@@ -342,6 +342,7 @@
 import errno
 import grp
 import os
+import re
 import platform
 import pwd
 import shutil
@@ -358,6 +359,9 @@
     HAVE_SPWD = False
 
 
+_HASH_RE = re.compile(r'[^a-zA-Z0-9./=]')
+
+
 class User(object):
     """
     This is a generic User manipulation class that is subclassed
@@ -429,6 +433,37 @@ def __init__(self, module):
         else:
             self.ssh_file = os.path.join('.ssh', 'id_%s' % self.ssh_type)
 
+    def check_password_encrypted(self):
+        # darwin need cleartext password, so no check
+        if self.module.params['password'] and self.platform != 'Darwin':
+            maybe_invalid = False
+            # : for delimiter, * for disable user, ! for lock user
+            # these characters are invalid in the password
+            if any(char in self.module.params['password'] for char in ':*!'):
+                maybe_invalid = True
+            if '$' not in self.module.params['password']:
+                maybe_invalid = True
+            else:
+                fields = self.module.params['password'].split("$")
+                if len(fields) >= 3:
+                    # contains character outside the crypto constraint
+                    if bool(_HASH_RE.search(fields[-1])):
+                        maybe_invalid = True
+                    # md5
+                    if fields[1] == '1' and len(fields[-1]) != 22:
+                        maybe_invalid = True
+                    # sha256
+                    if fields[1] == '5' and len(fields[-1]) != 43:
+                        maybe_invalid = True
+                    # sha512
+                    if fields[1] == '6' and len(fields[-1]) != 86:
+                        maybe_invalid = True
+                else:
+                    maybe_invalid = True
+            if maybe_invalid:
+                self.module.warn("The input password appears not to have been hashed. "
+                                 "The 'password' argument must be encrypted for this module to work properly.")
+
     def execute_command(self, cmd, use_unsafe_shell=False, data=None, obey_checkmode=True):
         if self.module.check_mode and obey_checkmode:
             self.module.debug('In check mode, would have run: "%s"' % cmd)
@@ -2392,6 +2427,7 @@ def main():
     )
 
     user = User(module)
+    user.check_password_encrypted()
 
     module.debug('User instantiated - platform %s' % user.platform)
     if user.distribution:


Test patch:
diff --git a/test/integration/targets/user/tasks/main.yml b/test/integration/targets/user/tasks/main.yml
--- a/test/integration/targets/user/tasks/main.yml
+++ b/test/integration/targets/user/tasks/main.yml
@@ -64,6 +64,46 @@
       - user_test0_1 is not changed
       - '"ansibulluser" in user_names.stdout_lines'
 
+# test user add with password
+- name: add an encrypted password for user
+  user:
+    name: ansibulluser
+    password: "$6$rounds=656000$TT4O7jz2M57npccl$33LF6FcUMSW11qrESXL1HX0BS.bsiT6aenFLLiVpsQh6hDtI9pJh5iY7x8J7ePkN4fP8hmElidHXaeD51pbGS."
+    state: present
+    update_password: always
+  register: test_user_encrypt0
+
+- name: there should not be warnings
+  assert:
+    that: "'warnings' not in test_user_encrypt0"
+
+- block:
+    - name: add an plaintext password for user
+      user:
+        name: ansibulluser
+        password: "plaintextpassword"
+        state: present
+        update_password: always
+      register: test_user_encrypt1
+
+    - name: there should be a warning complains that the password is plaintext
+      assert:
+        that: "'warnings' in test_user_encrypt1"
+
+    - name: add an invalid hashed password
+      user:
+        name: ansibulluser
+        password: "$6$rounds=656000$tgK3gYTyRLUmhyv2$lAFrYUQwn7E6VsjPOwQwoSx30lmpiU9r/E0Al7tzKrR9mkodcMEZGe9OXD0H/clOn6qdsUnaL4zefy5fG+++++"
+        state: present
+        update_password: always
+      register: test_user_encrypt2
+
+    - name: there should be a warning complains about the character set of password
+      assert:
+        that: "'warnings' in test_user_encrypt2"
+  when: ansible_system != 'Darwin'
+
+
 # https://github.com/ansible/ansible/issues/42484
 # Skipping macOS for now since there is a bug when changing home directory
 - block: