问题描述
在使用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