1

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

2
  • looks like you need a stat before the loop, and then a when conditional based on the outcome of that? Commented Apr 9, 2020 at 11:17
  • 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. Commented Apr 9, 2020 at 12:03

2 Answers 2

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.

2

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

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.