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