Ansible
如何在ansible中使用循環寄存器結果作為條件?
我想使用劇本為多個使用者創建個人資料。但它總是向我顯示錯誤
list object has no element 2
。這是劇本:vars: users: - test1 - test2 tasks: - name: Check user group command: id -ng "{{ item }}" register: users_group changed_when: false loop: '{{ users | default([], true) }}' - name: Check user home dir shell: cat /etc/passwd | grep "{{ item }}" | cut -d ":" -f6 loop: '{{ users | default([], true) }}' register: users_home changed_when: false - name: Touch user profile file: path: '{{ item[0].stdout }}/.profile' owner: '{{ item[1] }}' group: '{{ item[2].stdout }}' mode: '0644' state: touch when: item[2].rc == 0 loop: '{{ users_home.results | zip_longest(users,users_group.results) }}'
我已經使用調試檢查了循環項目。項目結構已經到
[[{...}]]
。所以循環只有一個項目,劇本不起作用。有沒有辦法做到這一點。
給定用於測試的使用者列表
users: [user1, user2, user3]
從註冊的變數創建字典。例如,將以下聲明放入vars
stats: "{{ users_group.results|json_query('[].{rc: rc, group: stdout}') }}" users_stats: "{{ dict(users|zip(stats)) }}" homes: "{{ users_home.results|json_query('[].stdout') }}" users_homes: "{{ dict(users|zip(homes)) }}"
給
users_stats: user1: {group: user1, rc: 0} user2: {group: user2, rc: 0} user3: {group: '', rc: 1}
users_homes: user1: /home/user1 user2: /home/user2 user3: ''
然後,下面的任務做你想要的
- name: Touch user profile file: state: touch path: '{{ users_homes[item] }}/.profile' owner: '{{ item }}' group: '{{ users_stats[item].group }}' mode: '0644' loop: '{{ users }}' when: users_stats[item].rc == 0
筆記
用於測試的完整劇本範例
- hosts: localhost become: true vars: users: [user1, user2, user3] stats: "{{ users_group.results|json_query('[].{rc: rc, group: stdout}') }}" users_stats: "{{ dict(users|zip(stats)) }}" homes: "{{ users_home.results|json_query('[].stdout') }}" users_homes: "{{ dict(users|zip(homes)) }}" tasks: - name: Check user group command: id -ng "{{ item }}" loop: '{{ users|default([], true) }}' register: users_group changed_when: false ignore_errors: true - debug: var: users_stats - name: Check user home dir shell: cat /etc/passwd | grep "{{ item }}" | cut -d ":" -f6 loop: '{{ users | default([], true) }}' register: users_home changed_when: false - debug: var: users_homes - name: Touch user profile file: state: touch path: '{{ users_homes[item] }}/.profile' owner: '{{ item }}' group: '{{ users_stats[item].group }}' mode: '0644' loop: '{{ users }}' when: users_stats[item].rc == 0
給
PLAY [localhost] ***************************************************************************** TASK [Check user group] ********************************************************************** ok: [localhost] => (item=user1) ok: [localhost] => (item=user2) failed: [localhost] (item=user3) => changed=false ansible_loop_var: item cmd: - id - -ng - user3 delta: '0:00:00.003991' end: '2022-09-03 23:19:42.953581' item: user3 msg: non-zero return code rc: 1 start: '2022-09-03 23:19:42.949590' stderr: 'id: ‘user3’: no such user' stderr_lines: <omitted> stdout: '' stdout_lines: <omitted> ...ignoring TASK [debug] ********************************************************************************* ok: [localhost] => users_stats|to_yaml: |- user1: {group: user1, rc: 0} user2: {group: user2, rc: 0} user3: {group: '', rc: 1} TASK [Check user home dir] ******************************************************************* ok: [localhost] => (item=user1) ok: [localhost] => (item=user2) ok: [localhost] => (item=user3) TASK [debug] ********************************************************************************* ok: [localhost] => users_homes: user1: /home/user1 user2: /home/user2 user3: '' TASK [Touch user profile] ******************************************************************** changed: [localhost] => (item=user1) changed: [localhost] => (item=user2) skipping: [localhost] => (item=user3) PLAY RECAP *********************************************************************************** localhost: ok=5 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=1
任務不是冪等的
每次執行劇本時都會觸及這些文件
TASK [Touch user profile] ******************************************************************** changed: [localhost] => (item=user1) changed: [localhost] => (item=user2) skipping: [localhost] => (item=user3)
如果要使此任務具有冪等性,請將參數access_time和modify_time設置為“保留”
- name: Touch user profile file: state: touch path: '{{ users_homes[item] }}/.profile' owner: '{{ item }}' group: '{{ users_stats[item].group }}' mode: '0644' access_time: preserve modification_time: preserve loop: '{{ users }}' when: users_stats[item].rc == 0
使用模組 getent
簡化程式碼並使用模組getent而不是自己讀取和解析數據庫。例如,以下任務分別創建字典getent_passwd和getent_group
- getent: database: passwd - getent: database: group
創建一個id為group的字典。將以下聲明放入vars
id_groups: "{{ dict(getent_group.values()|map(attribute=1)|list| zip(getent_group.keys()|list)) }}"
給
id_groups: '0': root '1': daemon '10': uucp '100': users ...
然後,下面的任務做你想要的
- name: Touch user profile file: state: touch path: '{{ getent_passwd[item].4 }}/.profile' owner: '{{ item }}' group: '{{ id_groups[getent_passwd[item].2] }}' mode: '0644' access_time: preserve modification_time: preserve loop: '{{ users }}' when: item in getent_passwd
用於測試的完整劇本範例
- hosts: localhost become: true vars: users: [user1, user2, user3] id_groups: "{{ dict(getent_group.values()|map(attribute=1)|list| zip(getent_group.keys()|list)) }}" tasks: - getent: database: passwd - getent: database: group - debug: var: id_groups - name: Touch user profile file: state: touch path: '{{ getent_passwd[item].4 }}/.profile' owner: '{{ item }}' group: '{{ id_groups[getent_passwd[item].2] }}' mode: '0644' access_time: preserve modification_time: preserve loop: '{{ users }}' when: item in getent_passwd