<p>I solved this with the Jinja test "exists" and the select filter to filter away the resulting 'AnsibleUndefined' values. The "is exists" test checks if the path exists on the ansible control host. This is the place the credentials are stored.</p>

<p>The solved code is below:</p>

<pre class="lang-yaml prettyprint-override"><code>    ---
    - hosts: localhost
      tasks:
      - name: Include users.yaml and setup all vars for nexus
        include_vars:
          file: users.yaml
          name: users
      - set_fact:
          tmp_user:
          - username: "{{ item.username }}"
            first_name: "{{ item.first_name }}"
            last_name: "{{ item.last_name }}"
            email: "{{ item.email }}"
            password: "{{ lookup('password', 'credentials/' + item.username + '/password.txt length=15 chars=ascii_letters') }}"
            roles: "{{ item.roles }}"
        loop: "{{ users.nexus_local_users }}"
        register: tmp_users
        when: "not '{{ playbook_dir }}/credentials/{{ item.username }}/password.txt' is exists"
      - set_fact:
          tmp_user: "{{ item.ansible_facts.tmp_user | combine(item.item) }}"
        with_items: "{{ tmp_users.results }}"
        register: tmp_users_sorted
        when: "not item is skipped"
      - set_fact:
          nexus_local_users: "{{ tmp_users_sorted.results | map(attribute='item.ansible_facts.tmp_user') | list | select('defined') | list }}"
      - debug:
          msg: "{{ nexus_local_users }}"
</code></pre>

<p>The following was added/changed:</p>

<pre class="lang-yaml prettyprint-override"><code>        when: "not '{{ playbook_dir }}/credentials/{{ item.username }}/password.txt' is exists"
</code></pre>

<pre class="lang-yaml prettyprint-override"><code>        nexus_local_users: "{{ tmp_users_sorted.results | map(attribute='item.ansible_facts.tmp_user') | list | select('defined') | list }}"
</code></pre>

<pre class="lang-yaml prettyprint-override"><code>       when: "not item is skipped"
</code></pre>

<p>This makes for a compact solution without adding more tasks/files.</p>

<p>For more information see: <a href="https://docs.ansible.com/ansible/latest/user_guide/playbooks_tests.html" rel="nofollow noreferrer">https://docs.ansible.com/ansible/latest/user_guide/playbooks_tests.html</a></p>

CC BY-SA 4.0
2020-04-10T10:00:39.637Z
2020-04-10T11:48:56.230Z
2020-04-10T11:48:56.230Z
Gijs Peerlings
<p>You should first <a href="https://docs.ansible.com/ansible/latest/modules/stat_module.htm" rel="nofollow noreferrer">stat</a> for the password file, and then set the fact depending on whether the <a href="https://docs.ansible.com/ansible/latest/modules/stat_module.html#return-stat/exists" rel="nofollow noreferrer">file.stat.exists</a> or not. And both those tasks need to be performed for each user.</p>

<p>As of Ansible 2.4 you can dynamically <a href="https://docs.ansible.com/ansible/latest/modules/include_tasks_module.html" rel="nofollow noreferrer">include_tasks</a> and perform a <a href="https://docs.ansible.com/ansible/latest/user_guide/playbooks_loops.html" rel="nofollow noreferrer">loop</a> on it. Meaning that you can group a few tasks in a file and loop those tasks for every value in your loop-variable.</p>

<p>I think this should be able to solve your problem by creating a separate tasks file containing these tasks that should be run for each user:</p>

<p>Stat for the password-file:</p>

<pre class="lang-yaml prettyprint-override"><code>   - name: Check if password-file exists for user
     stat:
        path: "credentials/{{ item.username }}/password.txt"
     register: password_file
</code></pre>

<p>Next you can define your tmp_user var when the password.txt-file exists:</p>

<pre class="lang-yaml prettyprint-override"><code>   - name: Set fact for user
     set_fact:
       tmp_user:
        - username: "{{ item.username }}"
          first_name: "{{ item.first_name }}"
          last_name: "{{ item.last_name }}"
          email: "{{ item.email }}"
          password: "{{ lookup('password', 'credentials/' + item.username + '/password.txt length=15 chars=ascii_letters') }}"
          roles: "{{ item.roles }}"
      when: password_file.stat.exists
      register: set_tmp_user
</code></pre>

<p>Then, if previous task defined a new fact tmp_user, that fact should be added to the tmp_users list. </p>

<pre class="lang-yaml prettyprint-override"><code>   - name: Add user to list
     set_fact:
       tmp_users: {{ tmp_users + [ tmp_user ] }}
     when: not set_tmp_user is skipped
</code></pre>

<p>(not sure by head if you can use <code>when: set_tmp_user is changed</code> but <code>not is skipped</code> should do the job)</p>

<p>Then in you main tasks-file, you create an empty tmp_users list and call an include_tasks in a loop to loop through the above tasks:</p>

<pre class="lang-yaml prettyprint-override"><code>   - name: Include users.yaml and setup all vars for nexus
     include_vars:
       file: users.yaml
       name: users

   - name: Initialize tmp_users
     set_fact:
        tmp_users: []

   - name: Set user facts
     include_tasks: set_user_facts.yml
     loop: "{{ users.nexus_local_users }}"
</code></pre>

<p>Disclaimer: I have not been able to test this out, or check its syntax, but I assume the general idea of which I try to accomplish here should be clear?</p>

<p>Also be aware that you currently can't perform a loop inside the included tasks file as that loop will by default overwrite the <code>item</code>-variable defined by the tasks-loop. See <a href="https://docs.ansible.com/ansible/latest/user_guide/playbooks_loops.html#defining-inner-and-outer-variable-names-with-loop-var" rel="nofollow noreferrer">defining inner and outer variable names with loop var</a> to solve this.</p>

<p>And an additional tip: add a debug task as first task to your included tasks file to output the current <code>item.username</code> in the loop.</p>

CC BY-SA 4.0
2020-04-09T15:38:52.253Z
2020-04-09T15:38:52.253Z
Robin Roevens
<p>I have written some Ansible code to put users from the users.yaml file in the variable 'nexus_local_users' to be used by a role. When doing this a random password is created and written to the 'credentials/{{ user }}/password.txt' file. </p>

<p>I'm looking for a condition that can make sure that once the user is created it won't be added to the variable again, to make sure the user isn't added twice. My first thought to achieve this was to check if a password has been generated already. </p>

<p>Is there any way to make sure a user is only added to this variable once and not on consecutive runs?</p>

<p>Here is a snapshot of the code I am using:</p>

<pre><code>    - name: Include users.yaml and setup all vars for nexus
      include_vars:
        file: users.yaml
        name: users
    - set_fact:
       tmp_user:
        - username: "{{ item.username }}"
          first_name: "{{ item.first_name }}"
          last_name: "{{ item.last_name }}"
          email: "{{ item.email }}"
          password: "{{ lookup('password', 'credentials/' + item.username + '/password.txt length=15 chars=ascii_letters') }}"
          roles: "{{ item.roles }}"
      loop: "{{ users.nexus_local_users }}" 
      register: tmp_users
    - set_fact:
        tmp_user: "{{ item.item | combine(item.ansible_facts.tmp_user) }}"
      with_items: "{{ tmp_users.results }}"
      register: tmp_users
    - set_fact:
        nexus_local_users: "{{ tmp_users.results | map(attribute='ansible_facts.tmp_user') | list }}"
</code></pre>

<p>I'm looking for something like this but can't figure out how to implement it (the when condition):</p>

<pre><code>    - set_fact:
       tmp_user:
        - username: "{{ item.username }}"
          first_name: "{{ item.first_name }}"
          last_name: "{{ item.last_name }}"
          email: "{{ item.email }}"
          password: "{{ lookup('password', 'credentials/' + item.username + '/password.txt length=15 chars=ascii_letters') }}"
          roles: "{{ item.roles }}"
      loop: "{{ users.nexus_local_users }}"
      when: **directory/file credentials/{{ user }}/password.txt exists**
      register: tmp_users
</code></pre>

<p>Thanks in advance!</p>

<p>Gijs</p>

CC BY-SA 4.0
2020-04-09T11:17:38.460Z
looks like you need a `stat` before the loop, and then a when conditional based on the outcome of that?
Bruce Becker
CC BY-SA 4.0
2020-04-09T12:03:54.393Z
Can I loop the `stat` as well? It needs to check all the directory's and based on that figure out if the user was already created. This means there is a directory per user in the credentials folder if that user was already created.
Gijs Peerlings
<ansible><automation><passwords>
Ansible: When 'directory/file exists' in a loop
Gijs Peerlings
# Ansible: When 'directory/file exists' in a loop

(Asked by: username_0 on 2020-04-09)

I have written some Ansible code to put users from the users.yaml file in the variable 'nexus_local_users' to be used by a role. When doing this a random password is created and written to the 'credentials/{{ user }}/password.txt' file. 
I'm looking for a condition that can make sure that once the user is created it won't be added to the variable again, to make sure the user isn't added twice. My first thought to achieve this was to check if a password has been generated already. 
Is there any way to make sure a user is only added to this variable once and not on consecutive runs?
Here is a snapshot of the code I am using:
    - name: Include users.yaml and setup all vars for nexus
      include_vars:
        file: users.yaml
        name: users
    - set_fact:
       tmp_user:
        - username: "{{ item.username }}"
          first_name: "{{ item.first_name }}"
          last_name: "{{ item.last_name }}"
          email: "{{ item.email }}"
          password: "{{ lookup('password', 'credentials/' + item.username + '/password.txt length=15 chars=ascii_letters') }}"
          roles: "{{ item.roles }}"
      loop: "{{ users.nexus_local_users }}" 
      register: tmp_users
    - set_fact:
        tmp_user: "{{ item.item | combine(item.ansible_facts.tmp_user) }}"
      with_items: "{{ tmp_users.results }}"
      register: tmp_users
    - set_fact:
        nexus_local_users: "{{ tmp_users.results | map(attribute='ansible_facts.tmp_user') | list }}"

I'm looking for something like this but can't figure out how to implement it (the when condition):
    - set_fact:
       tmp_user:
        - username: "{{ item.username }}"
          first_name: "{{ item.first_name }}"
          last_name: "{{ item.last_name }}"
          email: "{{ item.email }}"
          password: "{{ lookup('password', 'credentials/' + item.username + '/password.txt length=15 chars=ascii_letters') }}"
          roles: "{{ item.roles }}"
      loop: "{{ users.nexus_local_users }}"
      when: **directory/file credentials/{{ user }}/password.txt exists**
      register: tmp_users

Thanks in advance!
Gijs

**Tags:** ansible, automation, passwords

> **Comments:**
> **username_1**: looks like you need a `stat` before the loop, and then a when conditional based on the outcome of that?  
> **username_0**: Can I loop the `stat` as well? It needs to check all the directory's and based on that figure out if the user was already created. This means there is a directory per user in the credentials folder if that user was already created.  

---

## 2 answers

### Answer by username_0 on 2020-04-10. Score: 2 | Accepted answer

I solved this with the Jinja test "exists" and the select filter to filter away the resulting 'AnsibleUndefined' values. The "is exists" test checks if the path exists on the ansible control host. This is the place the credentials are stored.
The solved code is below:
    ---
    - hosts: localhost
      tasks:
      - name: Include users.yaml and setup all vars for nexus
        include_vars:
          file: users.yaml
          name: users
      - set_fact:
          tmp_user:
          - username: "{{ item.username }}"
            first_name: "{{ item.first_name }}"
            last_name: "{{ item.last_name }}"
            email: "{{ item.email }}"
            password: "{{ lookup('password', 'credentials/' + item.username + '/password.txt length=15 chars=ascii_letters') }}"
            roles: "{{ item.roles }}"
        loop: "{{ users.nexus_local_users }}"
        register: tmp_users
        when: "not '{{ playbook_dir }}/credentials/{{ item.username }}/password.txt' is exists"
      - set_fact:
          tmp_user: "{{ item.ansible_facts.tmp_user | combine(item.item) }}"
        with_items: "{{ tmp_users.results }}"
        register: tmp_users_sorted
        when: "not item is skipped"
      - set_fact:
          nexus_local_users: "{{ tmp_users_sorted.results | map(attribute='item.ansible_facts.tmp_user') | list | select('defined') | list }}"
      - debug:
          msg: "{{ nexus_local_users }}"

The following was added/changed:
        when: "not '{{ playbook_dir }}/credentials/{{ item.username }}/password.txt' is exists"

        nexus_local_users: "{{ tmp_users_sorted.results | map(attribute='item.ansible_facts.tmp_user') | list | select('defined') | list }}"

       when: "not item is skipped"

This makes for a compact solution without adding more tasks/files.
For more information see: https://docs.ansible.com/ansible/latest/user_guide/playbooks_tests.html


---

### Answer by username_2 on 2020-04-09. Score: 2

You should first stat for the password file, and then set the fact depending on whether the file.stat.exists or not. And both those tasks need to be performed for each user.
As of Ansible 2.4 you can dynamically include_tasks and perform a loop on it. Meaning that you can group a few tasks in a file and loop those tasks for every value in your loop-variable.
I think this should be able to solve your problem by creating a separate tasks file containing these tasks that should be run for each user:
Stat for the password-file:
   - name: Check if password-file exists for user
     stat:
        path: "credentials/{{ item.username }}/password.txt"
     register: password_file

Next you can define your tmp_user var when the password.txt-file exists:
   - name: Set fact for user
     set_fact:
       tmp_user:
        - username: "{{ item.username }}"
          first_name: "{{ item.first_name }}"
          last_name: "{{ item.last_name }}"
          email: "{{ item.email }}"
          password: "{{ lookup('password', 'credentials/' + item.username + '/password.txt length=15 chars=ascii_letters') }}"
          roles: "{{ item.roles }}"
      when: password_file.stat.exists
      register: set_tmp_user

Then, if previous task defined a new fact tmp_user, that fact should be added to the tmp_users list. 
   - name: Add user to list
     set_fact:
       tmp_users: {{ tmp_users + [ tmp_user ] }}
     when: not set_tmp_user is skipped

(not sure by head if you can use when: set_tmp_user is changed but not is skipped should do the job)
Then in you main tasks-file, you create an empty tmp_users list and call an include_tasks in a loop to loop through the above tasks:
   - name: Include users.yaml and setup all vars for nexus
     include_vars:
       file: users.yaml
       name: users

   - name: Initialize tmp_users
     set_fact:
        tmp_users: []

   - name: Set user facts
     include_tasks: set_user_facts.yml
     loop: "{{ users.nexus_local_users }}"

Disclaimer: I have not been able to test this out, or check its syntax, but I assume the general idea of which I try to accomplish here should be clear?
Also be aware that you currently can't perform a loop inside the included tasks file as that loop will by default overwrite the item-variable defined by the tasks-loop. See defining inner and outer variable names with loop var to solve this.
And an additional tip: add a debug task as first task to your included tasks file to output the current item.username in the loop.


---

