Ansible循环中的’目录/文件存在’

56次阅读
没有评论

问题描述

在使用Ansible时,希望在循环中检查目录/文件是否存在。他已经编写了一些Ansible代码,将users.yaml文件中的用户放入变量’nexus_local_users’中,以供角色使用。在这个过程中,会生成一个随机密码,并将其写入’credentials/{{ user }}/password.txt’文件中。
用户希望有一种条件,确保一旦用户被创建,就不会再次添加到变量中,以确保用户不会被重复添加。他最初的想法是检查是否已经生成了密码。
有没有办法确保用户只被添加到这个变量一次,而不是在连续运行中重复添加?
以下是他使用的代码示例:

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

用户希望实现以下类似的功能,但不知道如何实现(when条件):

- 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: **目录/文件 credentials/{{ user }}/password.txt 存在**
  register: tmp_users

提前感谢您的帮助!
Gijs

解决方案

请注意以下操作注意版本差异及修改前做好备份。

方案1

你可以使用Ansible的stat模块在循环之前检查密码文件是否存在,并根据file.stat.exists的结果设置变量。这两个任务都需要对每个用户执行。
从Ansible 2.4开始,你可以动态地使用include_tasks模块,并对其进行循环。这意味着你可以将几个任务分组到一个文件中,并对每个循环变量的值循环执行这些任务。
以下是一个解决方案示例,其中包含了应该为每个用户运行的这些任务的单独任务文件:
检查密码文件是否存在:

- name: Check if password-file exists for user
  stat:
    path: "credentials/{{ item.username }}/password.txt"
  register: password_file

如果密码文件存在,你可以定义tmp_user变量:

- 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

然后,如果前一个任务定义了新的tmp_user变量,该变量应该添加到tmp_users列表中。

- name: Add user to list
  set_fact:
    tmp_users: {{ tmp_users + [ tmp_user ] }}
  when: not set_tmp_user is skipped

请注意,我不确定是否可以使用when: set_tmp_user is changed,但not is skipped应该可以完成任务。
然后,在你的主任务文件中,创建一个空的tmp_users列表,并在循环中调用include_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 }}"

免责声明:我无法测试这个解决方案或检查其语法,但我认为我在这里尝试解决的一般思路应该是清楚的。
另外一个提示:在你的包含任务文件的第一个任务中添加一个debug任务,输出当前循环中的item.username。

方案2

使用Jinja测试”exists”和select过滤器来过滤掉结果为’AnsibleUndefined’的值,我解决了这个问题。”is exists”测试检查ansible控制主机上的路径是否存在。这是凭证存储的地方。
以下是已添加/更改的内容:

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

这样可以得到一个紧凑的解决方案,而不需要添加更多的任务/文件。
更多信息请参考:https://docs.ansible.com/ansible/latest/user_guide/playbooks_tests.html

正文完