在我的剧本中,我想获取 UI 高于 14000 的所有本地用户的列表
我在 /etc/passwd 或 ansible.builtin.getent 上使用了 ansible.builtin.slurp 但真正的问题是我得到的 UID 值是字符串,而不是 int,所以我无法正确过滤并且我得到的所有结果都是空的,或抱怨因为不是整数。
- name: Get all users from /etc/passwd
ansible.builtin.getent:
database: passwd
register: users_info
- name: Filter users with UID greater than 14000
set_fact:
filtered_users: >-
{{
users_info.ansible_facts.getent_passwd |
dict2items |
selectattr('value.1', 'int') |
selectattr('value.1', >, 14000) |
map(attribute='key') |
list
}}
我以为
selectattr('value.1', 'int')
会转换为int,但结果总是空的。 (我的字典里确实有一些超过14000的UID)
这里是getent提供的用户字典,所有UID都被引用,所以它们是字符串
root:
- x
- '0'
- '0'
- root
- /root
- /bin/bash
shutdown:
- x
- '6'
- '0'
- shutdown
- /sbin
- /sbin/shutdown
sshd:
- x
- '74'
- '74'
- Privilege-separated SSH
- /usr/share/empty.sshd
- /sbin/nologin
我没有办法尝试,除了运行一些我会避免的 shell cmd。
我发现您的过滤器链存在几个问题:
filtered_users: >-
{{
users_info.ansible_facts.getent_passwd |
dict2items |
selectattr('value.1', 'int') |
selectattr('value.1', >, 14000) |
map(attribute='key') |
list
}}
首先,虽然有一个名为
int
的过滤器,但没有名为
integer
的测试。不幸的是,这是一个 test,而不是转换过滤器,因此由于在所有情况下 value.1
的值都是字符串,因此此 selectattr()
过滤器将拒绝您的所有条目,从而导致空列表。此点之后的所有内容都无关紧要,因为没有任何内容可以过滤。
例如:
- hosts: localhost
gather_facts: false
vars:
example:
- val: '1'
- val: '2'
- val: '3'
tasks:
- debug:
msg: "{{ example | selectattr('val', 'integer') }}"
结果:
ok: [localhost] => {
"msg": []
}
json_query
过滤器 更简单的解决方案。对于您想要的,我们可以这样做:
- name: Filter users with UID > 1000
set_fact:
filtered_users: >-
{{
users_info.ansible_facts.getent_passwd |
dict2items |
json_query('[?to_number(value[1]) > `1000`].key')
}}
to_number
函数 将字符串值转换为整数进行比较。
在我的系统上,这会产生:
ok: [localhost] => {
"filtered_users": [
"nobody"
]
}
使用 community.general.jc 解析文件 /etc/passwd。宣布目的地
dest_dir: /tmp/ansible/fetch
并获取文件
- fetch:
src: /etc/passwd
dest: "{{ dest_dir }}"
声明路径并解析文件
passwd_path: "{{ dest_dir }}/{{ inventory_hostname }}/etc/passwd"
my_users: "{{ lookup('file', passwd_path) | community.general.jc('passwd') }}"
给出(删节)
my_users:
- comment: Charlie &
gid: 0
home: /root
password: '*'
shell: /bin/sh
uid: 0
username: root
- comment: Bourne-again Superuser
...
属性 uid 和 gid 是整数。你可以测试一下
- debug:
msg: |
{{ my_users.0.username }}
{{ my_users.0.uid }}
{{ my_users.0.uid | type_debug }}
给予
msg: |-
root
0
int
现在,选择按预期进行了
filtered_users: "{{ my_users |
selectattr('uid', '>', 14000) |
map(attribute='username') }}"
给予
filtered_users:
- nobody
- hosts: host_a
vars:
dest_dir: /tmp/ansible/fetch
passwd_path: "{{ dest_dir }}/{{ inventory_hostname }}/etc/passwd"
my_users: "{{ lookup('file', passwd_path) | community.general.jc('passwd') }}"
filtered_users: "{{ my_users | selectattr('uid', '>', 14000) | map(attribute='username') }}"
tasks:
- fetch:
src: /etc/passwd
dest: "{{ dest_dir }}"
- debug:
var: my_users
- debug:
msg: |
{{ my_users.0.username }}
{{ my_users.0.uid }}
{{ my_users.0.uid | type_debug }}
- debug:
var: filtered_users
将声明放入group_vars
shell> cat group_vars/all.yml
dest_dir: /tmp/ansible/fetch
passwd_path: "{{ dest_dir }}/{{ inventory_hostname }}/etc/passwd"
my_users: "{{ lookup('file', passwd_path) | community.general.jc('passwd') }}"
uid_min: 0
uid_max: 65535
filtered_users_min: "{{ my_users | selectattr('uid', '>=', uid_min|int) | map(attribute='username') }}"
filtered_users_max: "{{ my_users | selectattr('uid', '<=', uid_max|int) | map(attribute='username') }}"
filtered_users: "{{ filtered_users_min | intersect(filtered_users_max) }}"
在剧本中,获取文件并声明间隔。例如,
shell> cat playbook.yml
---
- hosts: host_a
tasks:
- fetch:
src: /etc/passwd
dest: "{{ dest_dir }}"
- debug:
var: filtered_users
vars:
uid_min: 14000
感谢 @larsks 的回答,我能够修改查询以过滤 14000-15000 UID,如下所示,这给出了预期的结果:
- name: Filter users 14k-15k range
set_fact:
filtered_users: >-
{{
users_info.ansible_facts.getent_passwd |
dict2items |
json_query('[?to_number(value[1]) > `14000` && to_number(value[1]) <= `15000`].key')
}}