Ansible

如何在ansible中使用循環寄存器結果作為條件?

  • September 3, 2022

我想使用劇本為多個使用者創建個人資料。但它總是向我顯示錯誤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_timemodify_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_passwdgetent_group

   - getent:
       database: passwd
   - getent:
       database: group

創建一個idgroup的字典。將以下聲明放入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

引用自:https://serverfault.com/questions/1109878