Ansible读取CSV加密密码写入文件报错:'dict'无'stdout'属性
这个错误的核心原因是你在第二个任务中使用loop注册的output变量结构和第三个任务中调用的方式不匹配:
当任务使用loop并注册变量时,Ansible会返回一个包含results数组的对象,每个数组元素对应一次循环的执行结果(里面才包含stdout字段)。而你在第三个任务中直接调用output.stdout,但output本身并没有这个属性——它只有output.results,所以Ansible报错说dict object has no attribute 'stdout'。
同时,第三个任务的loop是独立遍历routers.list的,和第二个任务的循环没有直接关联,无法直接映射到对应的加密结果。
下面提供几种可靠的解决方案:
方案一:用循环索引关联结果(简单直接)
如果你的CSV文件顺序不会变化,可以通过循环的索引来匹配output.results中的对应项:
- name: Read csv file read_csv: path: files/ww.csv fieldnames: ip,password delimiter: ',' register: routers delegate_to: localhost - name: encrypt password command: "ansible-vault encrypt_string --vault-password-file password '{{ router.password }}' --name 'password'" loop: "{{ routers.list }}" loop_control: loop_var: router register: output delegate_to: localhost - name: write file copy: content: "{{ output.results[index].stdout }}" dest: "/tmp/{{ router.ip }}.yaml" loop: "{{ routers.list }}" loop_control: loop_var: router index_var: index # 获取当前循环的索引 delegate_to: localhost
这里通过index_var拿到当前循环的索引,用它去output.results里找到对应位置的加密结果,前提是两个循环的顺序完全一致。
方案二:将加密结果整理为字典(更可靠)
如果担心顺序变化或者需要更清晰的映射关系,可以把加密结果整理成一个以IP为键的字典,之后直接通过IP查找对应加密内容:
- name: Read csv file read_csv: path: files/ww.csv fieldnames: ip,password delimiter: ',' register: routers delegate_to: localhost - name: encrypt password command: "ansible-vault encrypt_string --vault-password-file password '{{ router.password }}' --name 'password'" loop: "{{ routers.list }}" loop_control: loop_var: router register: output delegate_to: localhost # 新增:把加密结果整理为 {IP: 加密后的内容} 的字典 - name: Organize encrypted passwords into a dictionary set_fact: encrypted_passwords: >- {{ encrypted_passwords | default({}) | combine({ item.item.ip: item.stdout }) }} loop: "{{ output.results }}" - name: write file copy: content: "{{ encrypted_passwords[router.ip] }}" dest: "/tmp/{{ router.ip }}.yaml" loop: "{{ routers.list }}" loop_control: loop_var: router delegate_to: localhost
这里item.item是因为output.results中的每个元素的item字段就是原来循环中的router对象,我们从中取出IP作为键,把加密后的stdout作为值存入字典,后续直接通过IP取值即可,完全不用依赖循环顺序。
方案三:合并加密与写入任务(结构更简洁)
你也可以把加密和写入操作放到同一个循环里,避免两次循环的关联问题:
- name: Read csv file read_csv: path: files/ww.csv fieldnames: ip,password delimiter: ',' register: routers delegate_to: localhost - name: Encrypt password and write to file block: - name: encrypt password command: "ansible-vault encrypt_string --vault-password-file password '{{ router.password }}' --name 'password'" register: encrypted_result delegate_to: localhost - name: write file copy: content: "{{ encrypted_result.stdout }}" dest: "/tmp/{{ router.ip }}.yaml" delegate_to: localhost loop: "{{ routers.list }}" loop_control: loop_var: router
这个方案中,每个路由器的加密和写入操作在同一个循环迭代中完成,逻辑更连贯,也不会出现变量关联的问题。
内容的提问来源于stack exchange,提问作者user3511320

