修复Ansible中Jinja2模板生成CSV时的字符串对象属性错误
问题原因
你遇到的错误核心是变量名冲突:在Build CSV File任务中,你将user_info_results重新定义为{{ user_info_results.results }},和之前任务注册的user_info_results变量重名,导致Ansible解析模板时该变量结构被意外破坏,原本的字典列表被解析为字符串,从而无法访问item属性。
此外还有两个潜在问题:
- 第一个任务的awk条件
$3 >= 999不符合你要求的UID≥1000,会错误包含UID=999的用户; Last_Login_Date的default('N/A')不会生效——因为lastlog返回空字符串时变量是已定义状态,default仅对未定义变量生效。
修复现有Playbook的步骤
1. 修正变量名冲突
在Build CSV File任务中更换变量名,避免和注册变量重名:
- name: Build CSV File ansible.builtin.template: src: "userlist_template.j2" dest: "output.csv" vars: users_passwd_data: "{{ user_info_results.results }}" users_sudo_data: "{{ sudo_status_results.results }}" users_login_data: "{{ last_login_results.results }}"
2. 对应修改Jinja2模板
调整模板中的变量引用,同时修复空登录日期的显示逻辑:
Hostname;Login;UID;GID;Comments;SUDO Enabled;Last_Login_Date {% for user_info in users_passwd_data %} {{ inventory_hostname }};{{ user_info['item'] }};{{ user_info.stdout.split(':')[2] }};{{ user_info.stdout.split(':')[3] }};{{ user_info.stdout.split(':')[4] }};{{ users_sudo_data[loop.index0].stdout }};{% if users_login_data[loop.index0].stdout | trim == '' %}N/A{% else %}{{ users_login_data[loop.index0].stdout }}{% endif %} {% endfor %}
3. 修正用户筛选条件
将第一个任务的awk条件改为$3 >= 1000,符合需求:
- name: Check User List shell: cmd: | awk -F ':' '{ if ($3 >= 1000 && $7 != "/sbin/nologin") print $1 }' /etc/passwd register: user_list_result
4. 确保sudo命令权限
在Check SUDO Privileges任务中添加become: yes,查询其他用户的sudo列表通常需要sudo权限:
- name: Check SUDO Privileges ansible.builtin.shell: "sudo -U {{ item.item }} -l | grep -q '(ALL) ALL' && echo 'YES' || echo 'NO'" loop: "{{ user_info_results.results }}" register: sudo_status_results become: yes
更优的Playbook(推荐)
使用Ansible内置的getent模块获取结构化的passwd数据,避免shell命令的字符串解析错误,更可靠且符合最佳实践:
--- - name: Generate user CSV report hosts: your_target_hosts gather_facts: no tasks: # 获取结构化的/etc/passwd数据 - name: Retrieve passwd entries ansible.builtin.getent: database: passwd split: ':' # 筛选符合条件的用户(UID≥1000,shell不是/sbin/nologin) - name: Filter target users ansible.builtin.set_fact: target_users: "{{ getent_passwd | dict2items | selectattr('value.2', '>=', 1000) | selectattr('value.6', '!=', '/sbin/nologin') | list }}" # 检查sudo权限 - name: Check sudo privileges ansible.builtin.command: "sudo -U {{ item.key }} -l | grep -q '(ALL) ALL' && echo 'YES' || echo 'NO'" loop: "{{ target_users }}" register: sudo_status become: yes # 获取最后登录日期 - name: Get last login date ansible.builtin.command: "lastlog -u {{ item.key }} | awk 'NR==2 {print $4}'" loop: "{{ target_users }}" register: last_login # 生成CSV文件 - name: Build CSV file ansible.builtin.template: src: userlist_template.j2 dest: "/tmp/user_report.csv"
对应的Jinja2模板:
Hostname;Login;UID;GID;Comments;SUDO Enabled;Last_Login_Date {% for user in target_users %} {{ inventory_hostname }};{{ user.key }};{{ user.value.2 }};{{ user.value.3 }};{{ user.value.4 }};{{ sudo_status.results[loop.index0].stdout }};{% if last_login.results[loop.index0].stdout | trim == '' %}N/A{% else %}{{ last_login.results[loop.index0].stdout }}{% endif %} {% endfor %}
内容的提问来源于stack exchange,提问作者Mnbaku_AJ
相关产品推荐
相关产品推荐

