Création d'un nouvel utilisateur et mot de passe avec Ansible

103

J'ai une tâche ansible qui crée un nouvel utilisateur sur ubuntu 12.04;

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

il se termine comme prévu, mais lorsque je me connecte en tant qu'utilisateur et que j'essaye de sudo avec le mot de passe que j'ai défini, il dit toujours que c'est incorrect. Qu'est-ce que je fais mal?

raphael_turtle
la source
1
Vous connectez-vous avec le même mot de passe ou les mêmes clés ssh? Avez-vous vérifié votre fichier shadow pour vous assurer que son contenu est conforme aux attentes? De plus, votre passwordn'est pas censé être en texte brut mais plutôt préhaché.
Mxx
mon mot de passe est en clair, puis-je ne pas l'utiliser de cette façon? Je ne comprends pas comment le chiffrer, ou j'en ai vraiment besoin.
raphael_turtle

Réponses:

101

Si vous lisez le manuel d'Ansible pour le usermodule , il vous dirigera vers le dépôt github Ansible-examples pour plus de détails sur l'utilisation du passwordparamètre .

Là, vous verrez que votre mot de passe doit être haché.

- 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}}

Si votre playbook ou votre ligne de commande ansible a votre mot de passe tel quel en texte brut, cela signifie que votre hachage de mot de passe enregistré dans votre fichier shadow est incorrect. Cela signifie que lorsque vous essayez de vous authentifier avec votre mot de passe, son hachage ne correspondra jamais.

De plus, consultez la FAQ Ansible concernant certaines nuances du paramètre de mot de passe et comment l'utiliser correctement.

Mxx
la source
25
Merci pour le conseil. Je voulais juste souligner que la documentation du module utilisateur liée ci-dessus recommande d'utiliser le openssl passwd -salt <salt> -1 <plaintext>pour générer le hachage du mot de passe, plutôt que le one-liner Python que vous avez ci-dessus. J'ai eu du mal à obtenir une sortie correcte de Python, probablement en raison de ma propre incompétence et la commande openssl fonctionnait mieux.
Brendan Wood
6
Comment synchronisez-vous le sel à utiliser avec le système d'exploitation?
Breedly
5
@Breedly: il n'y a pas besoin - le sel est toujours stocké dans le cadre du mot de passe ($ 1 $ thesalt $ thepasswordhash) ce qui signifie qu'il est portable entre les systèmes d'exploitation en utilisant la même fonction de hachage
Benjamin Dale
3
Utiliser cette commande python pour générer un hachage ne fonctionnait pas pour moi. Mais j'ai trouvé le hachage /etc/shadowaprès avoir défini le mot de passe manuellement en utilisant passwd <user>.
dokaspar
172

Je suis peut-être trop tard pour répondre à cela, mais j'ai récemment découvert que les filtres jinja2 avaient la capacité de gérer la génération de mots de passe cryptés. Dans mon, main.ymlje génère le mot de passe crypté comme:

- 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" et "upassword" sont passés comme --extra-varsau playbook et notez que j'ai utilisé le filtre jinja2 ici pour crypter le mot de passe passé.

J'ai ajouté ci-dessous le tutoriel lié à cela à mon blog

monstre de pensée
la source
6
Pour éviter que l'élément soit toujours "changé", vous pouvez ajouter un secret "salt" comme 2ème paramètre à password_hash.
Michael Wyraz
11
Selon la suggestion de @ MichaelWyraz: l'ajout d'un 2ème paramètre "sel" évite "changé". Vous pouvez définir cela via une variable, par exemple password={{upassword|password_hash('sha512', upassword_salt)}}. Cela vous permet de mettre du sel dans un coffre - fort de variables , comme vous le feriez probablement avec upasswordaussi, en gardant les deux hors du fichier tasks.yml.
user85461
J'appliquerais également la vérification d'état de @ bbaassssiiee et ajouterais update_password: on_createle module utilisateur à cette réponse pour empêcher l'expiration des mots de passe pour les utilisateurs déjà créés.
madhead
Merci pour votre excellent exemple, cela m'a amené sur la bonne voie. Néanmoins, j'ai dû faire d'autres choses pour le faire fonctionner sur un mac avec la version 2.8.2 ansible. Tout d'abord sur un mac, il n'est pas possible d'utiliser crypt donc il est nécessaire d'installer la bibliothèque passlib avec pip install passlib. Ensuite , pour pouvoir utiliser une voûte en ligne chaîne cryptée je devais reformater avec l'ajout suivant: password: {{ upassword | string | password_hash('sha512') }}. Cela évite le message d'erreursecret must be unicode or bytes, not ansible.parsing.yaml.objects.AnsibleVaultEncryptedUnicode
Michael Aicher
malgré l'utilisation de cette méthode pour définir un mot de passe: "{{my_password | string | password_hash ('sha512')}}" J'obtiendrai toujours - [AVERTISSEMENT]: le mot de passe d'entrée ne semble pas avoir été haché. L'argument 'password' doit être chiffré pour que ce module fonctionne correctement.
openCivilisation
46

Je souhaite proposer encore une autre 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

Pourquoi c'est mieux? Comme cela a déjà été noté ici, les jeux Ansible doivent être idempotents. Vous ne devriez pas les considérer comme une séquence d'actions dans un style impératif, mais comme un état souhaité, un style déclaratif. En conséquence, vous devriez pouvoir l'exécuter plusieurs fois et obtenir le même résultat, le même état de serveur.

Tout cela sonne bien, mais il y a quelques nuances. L'un d'eux est la gestion des utilisateurs. «État souhaité» signifie que chaque fois que vous exécutez une lecture qui crée un utilisateur, il sera mis à jour pour correspondre exactement à cet état. Par «mis à jour», je veux dire que son mot de passe sera également modifié. Mais ce n'est probablement pas ce dont vous avez besoin. Habituellement, vous devez créer un utilisateur, définir et expirer son mot de passe une seule fois, les autres parties ne doivent pas mettre à jour son mot de passe.

Heureusement, Ansible a un update_passwordattribut dans le usermodule qui résout ce problème. En mélangeant cela avec des variables enregistrées, vous pouvez également expirer son mot de passe uniquement lorsque l'utilisateur est réellement mis à jour.

Notez que si vous changez manuellement le shell de l'utilisateur (supposons que vous n'aimez pas le shell que l'administrateur maléfique a forcé dans son jeu), l'utilisateur sera mis à jour, donc son mot de passe sera expiré.

Notez également comment vous pouvez facilement utiliser les mots de passe initiaux en texte brut dans les jeux. Pas besoin de les encoder ailleurs et de coller des hachages, vous pouvez utiliser le filtre Jinja2 pour cela. Cependant, cela peut être une faille de sécurité si quelqu'un se connecte avant vous.

taré
la source
3
Je ne pouvais pas faire fonctionner l'autre solution pour moi. Pourtant, votre réponse est simple et efficace. J'aimerais vous donner 5 votes positifs, si vous le permettez.
leesei
Je ne veux pas que mon mot de passe soit codé en dur comme ça: (Est-il possible de le sortir du coffre-fort ansible et de l'injecter ici? L'imbrication "{{ '{{vaulted_password}}' | password_hash('sha512') }}"ne semble pas fonctionner ...
dokaspar
Avez-vous essayé {{ vaulted_password | password_hash('sha512') }}, où vaulted_passwordest une clé de la valeur dans le coffre-fort?
madhead
update_password: on_createne semble pas fonctionner (il y a un bogue ouvert à ce sujet à partir de 2017), donc les mots de passe changeront chaque fois qu'il y a un changement d'état sur un utilisateur.
Diti
11

Le module «utilisateur» d'Ansible gère les utilisateurs, de manière idempotente . Dans le playbook ci-dessous, la première tâche déclare state = present pour l'utilisateur. Notez que « register: newuser » dans la première action aide la deuxième action à déterminer si l'utilisateur est nouveau (newuser.changed == True) ou existant ( newuser.changed==False), pour ne générer le mot de passe qu'une seule fois.

Le playbook Ansible comprend:

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
bbaassssiiee
la source
Une seule solution qui fonctionne pour moi avec le mot de passe du coffre-fort. Merci beaucoup.
Denis Savenko
au moins les distributions récentes basées sur Debian ne semblent pas prendre en charge l'option longue GNU "--stdin" dans le binaire passwd.
XXL
10

essaie comme ça

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"
Artem Feofanov
la source
2
J'aime cette solution étant donné qu'elle permet de taper un mot de passe lorsque le script s'exécute. Belle solution pour un script bootstrap qui ne devrait pas être exécuté à chaque fois. Pour Ubuntu, j'ai cependant utilisé "sha512_crypt".
Gunnar
6

L'objectif du rôle dans cette réponse est de générer un mot de passe aléatoire pour new_user_name et d'expirer le mot de passe immédiatement. Le nouveau nom_utilisateur est requis pour changer le mot de passe lors de sa première connexion.

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

Lorsque vous souhaitez créer un nouvel utilisateur:

ansible-playbook -i hosts.ini create_user.yml --extra-vars "new_user_name=kelvin"
McKelvin
la source
3

Voici le moyen le plus simple:

---
- 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
Éclairé
la source
shell: provoquera toujours un rapport de changement.
bbaassssiiee
@datasmid vous pouvez ajouter l'option no_log: True docs.ansible.com/ansible/…
Phill Pafford
3

C'est comme ça que ça a fonctionné pour moi

- 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
hécbuma
la source
3

Juste pour être complet, je publierai la commande ad-hoc en utilisant ansible car il y a aussi un piège.

Essayez d'abord de générer un mot de passe chiffré à l'aide de l'utilitaire mkpasswd disponible sur la plupart des systèmes Linux:

mkpasswd --method=SHA-512

Ensuite, essayez la commande ansible ad-hock:

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

Mais assurez-vous que:

  1. La commande est entre guillemets simples et PAS double sinon votre mot de passe ne fonctionnera jamais
  2. Vous l'exécutez avec --sudoou vous vous retrouvez avec une erreur comme ( useradd: cannot lock /etc/passwd; try again later)
pgaref
la source
merci mon pote, je cherchais spécifiquement la version adhoc, et j'ai eu le même problème de citation que vous avez mentionné
alexakarpov
2

La définition de tâche pour le module utilisateur doit être différente dans la dernière version d'Ansible.

tasks:
  - user: name=test password={{ password }} state=present
Christian Berendt
la source
2

En combinant quelques solutions ci-dessus, j'ai créé un livre de lecture qui génère automatiquement des hachages de mot de passe corrects basés sur des mots de passe en texte clair stockés dans un fichier de coffre-fort local crypté:

---
- 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}}"

Exécutez cette commande en utilisant l'option "--ask-vault-pass" pour décrypter votre fichier de coffre-fort (voir ansible-vault pour plus d'informations sur la gestion d'un coffre-fort chiffré).

Steve Midgley
la source
2

Comment créer un mot de passe chiffré pour passer à passwordvar à la usertâche Ansible (à partir du commentaire de @Brendan Wood):

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

Le résultat ressemblera à:

$1$some_pla$lmVKJwdV3Baf.o.F0OOy71

Exemple de usertâche:

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

UPD: crypte utilisant SHA-512 voir ici et ici :

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/

Rubis

$ ruby -e 'puts "password".crypt("$6$saltsalt$")'

$6$saltsalt$qFmFH.bQmmtXzyBY0s9v7Oicd2z4XSIecDzlB5KiA2/jctKu9YterLp8wwnSq.qc.eoxqOmSuNp2xS0ktL3nh/
Dmitriy
la source
1
N'est-ce pas générer un mot de passe haché md5? N'est-ce pas incertain?
Marquer
2

Vous pouvez utiliser ansible-vault pour utiliser des clés secrètes dans les playbooks. Définissez votre mot de passe en yml.

ex. pass: secret ou

user:
  pass: secret
  name: fake

cryptez votre fichier secrets avec:

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

ansible demandera un mot de passe pour le crypter. (je vais vous expliquer comment utiliser ce pass)

Et puis vous pouvez utiliser vos variables où vous le souhaitez. Personne ne peut les lire sans clé de coffre-fort.

Utilisation de la clé du coffre-fort:

via un argument de passage lors de l'exécution de playbook.

--ask-vault-pass: secret

ou vous pouvez enregistrer dans un fichier comme password.txt et vous cacher quelque part. (utile pour les utilisateurs CI)

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

Dans votre cas: incluez vars yml et utilisez vos variables.

- include_vars: /path/credential.yml

  - name: Add deployment user
    action: user name={{user.name}} password={{user.pass}}
pmoksuz
la source
J'ai essayé exactement cela, et cela ne fonctionne pas. Je crois que c'est parce que password={{user.pass}}va s'étendre pour inclure le mot de passe réel, alors qu'ansible attend le hachage là-bas.
texnic
2

Générer un mot de passe aléatoire pour l'utilisateur

il faut d'abord définir la variable utilisateurs, puis suivre ci-dessous

Tâches:

- 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 }}'
Apuri Srikanth
la source
1

La réponse de Mxx est correcte, mais la crypt.crypt()méthode python n'est pas sûre lorsque différents systèmes d'exploitation sont impliqués (lié à l'algorithme de hachage glibc utilisé sur votre système.)

Par exemple, cela ne fonctionnera pas si vous générez votre hachage à partir de MacOS et exécutez un playbook sous Linux. Dans ce cas, vous pouvez utiliser passlib ( pip install passlibpour installer localement).

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

Aucune des solutions ne fonctionnait directement sur mon Mac contrôlant Ubuntu. Donc, pour le bien des autres, en combinant les réponses Mxx et JoelB, voici la solution actuelle de Python 3:

pip3 install passlib

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

Le résultat sera $1$SomeSalt$UqddPX3r4kH3UL5jq5/ZI., comme dans la réponse de Mxx.

Mieux encore , utilisez SHA512 au lieu de MD5:

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

Résultat:

6 $ tours = 656000 $ SomeSalt $ oYpmnpZahIsvn5FK8g4bDFEAmGpEN114Fe6Ko4HvinzFaz5Rq2UXQxoJZ9ZQyQoi9zaBo3gBH / FEAov3FHv48

texnic
la source
1

J'ai créé un livre de jeu ansible qui vous permet de créer un compte Linux qui permet l'authentification par mot de passe.

Voir CreateLinuxAccountWithAnsible .

Le mot de passe haché est généré à l'aide de la mkpasswdcommande. J'ai fourni les moyens d'installer mkpasswdsur différents systèmes d'exploitation.

Voici les étapes requises pour utiliser mon script:

  1. Remplacez <your_user_name>et à l' <your_password>intérieur run.shpar le nom d'utilisateur et le mot de passe souhaités.

  2. Modifiez les informations de connexion dans inventoryafin qu'ansible puisse se connecter à la machine pour créer un utilisateur.

  3. Exécutez ./run.shpour exécuter le script.

Brian
la source
1
Le fichier n'est plus sur GitHub.
Janus
0

Si vous souhaitez accomplir cela en tant que commande ad hoc Ansible, vous pouvez effectuer les opérations suivantes:

$ 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') }}\""

Sortie de la commande ci-dessus:

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
}
slm
la source
0

Je sais que je suis en retard à la fête, mais il y a une autre solution que j'utilise. Cela peut être pratique pour les distributions qui n'ont pas --stdinde binaire passwd.

- 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_controlest responsable de l'impression uniquement du nom d'utilisateur. L'ensemble du playbook ou simplement les variables utilisateur (que vous pouvez utiliser vars_files:) doivent être chiffrés avec ansible-vault.

Alex Baranowski
la source
0

Ma solution utilise la recherche et génère un mot de passe automatiquement.

---
- 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') }}"
Alex Naumov
la source
0

J'ai essayé de nombreux utilitaires, y compris mkpasswd, Python, etc. Mais il semble qu'il y ait un problème de compatibilité avec Ansible lors de la lecture des valeurs HASH générées par d'autres outils. Donc, finalement, cela a fonctionné par la valeur # ansible elle-même.

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"
DjangoChained
la source
-1

Eh bien, je suis totalement en retard pour faire la fête :) J'ai eu besoin d'un jeu ansible qui crée plusieurs utilisateurs locaux avec des mots de passe aléatoires. C'est ce que je suis venu avec, utilisé quelques exemples de haut et les mettre ensemble avec quelques changements.

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
Juhani
la source