如何在 ansible 中使用循环注册结果作为 when 条件?

如何在 ansible 中使用循环注册结果作为 when 条件?

我想使用剧本为多个用户创建个人资料。但是它总是显示错误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) }}'

我已经使用调试检查了循环项。项结构已变为[[{...}]]。因此循环只有一个项,剧本不起作用。有没有办法实现这一点。

答案1

给出测试用户列表

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

  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)

如果你想让这个任务幂等,请同时设置两个参数访问时间修改时间保存'

    - 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_group分别

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

创建词典ID一个团体. 将以下声明放入瓦尔斯

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

相关内容