Looping through a register variable

Good day, all. I hope I’m not wearing out my welcome with too many questions.

In the following playbook, I first find any know_host file for any user on a particular system. I then copy a file with the list of additions to add to the known_hosts files. What I want to do is use the output of my register values to make changes to those know_host files, which my last play does. My iteration is wrong, though.

The playbook:

You want stdout_lines rather than stdout. The former is a list with new-lines removed. The latter is a possibly very long string with the complete output stream intact.

    - name: Append to all known_host files
      shell: cat /tmp/append >> {{ item }}
      loop:
        - '{{ known.stdout_lines }}'

Todd, I made the change, but, the last play failed:

TASK [Append to all known_host files] *****************************************************************************************************
Thursday 23 May 2024 11:06:33 -0400 (0:00:01.629) 0:00:06.012 **********
Thursday 23 May 2024 11:06:33 -0400 (0:00:01.629) 0:00:06.011 **********
failed: [myhost] (item=[‘/root/.ssh/known_hosts’, ‘/home/user1/.ssh/known_hosts’, ‘/home/user2/.ssh/known_hosts’, ‘/home/user3/.ssh/known_hosts’, ‘/home/user4/.ssh/known_hosts’]) => changed=true
ansible_loop_var: item
cmd: cat /tmp/append >> [‘/root/.ssh/known_hosts’, ‘/home/user1/.ssh/known_hosts’, ‘/home/user2/.ssh/known_hosts’, ‘/home/user3/.ssh/known_hosts’, ‘/home/user4/.ssh/known_hosts’]
delta: ‘0:00:00.012330’
end: ‘2024-05-23 11:06:33.947617’
item:
- /root/.ssh/known_hosts
- /home/user1/.ssh/known_hosts
- /home/user2/.ssh/known_hosts
- /home/user3/.ssh/known_hosts
- /home/user4/.ssh/known_hosts
msg: non-zero return code
rc: 1
start: ‘2024-05-23 11:06:33.935287’
stderr: ‘/bin/sh: line 1: [/root/.ssh/known_hosts,: No such file or directory’
stderr_lines:
stdout: ‘’
stdout_lines:

I wonder if [ in [/root/.ssh/known_hosts is getting in the way, since /root/.ssh/known_host does exist.

Gah! Of course. known.stdout_lines is a list, so

loop:
  - '{{ known.stdout_lines }}'

passes the whole list as a single item. So you need to not put the list in a list:

loop: '{{ known.stdout_lines }}'

should do the trick.

Yupper, that worked. And, I appreciate the explanation. As the Farber College motto states, “Learning is Good”.

You want stdout_lines rather than stdout. The former is a list with new-lines removed. The latter is a possibly very long string with the complete output stream intact.

    - name: Append to all known_host files
      shell: cat /tmp/append >> {{ item }}
      loop:
        - '{{ known.stdout_lines }}'

That should probably just loop over the list itself? So:

Very subtle but very different. Great catch Todd.

In the first instance you create a list that contains a single item – another list called known.stdout_lines.

loop:

  • ‘{{ known.stdout_lines }}’

In the second instance you provide a list called known.stdout_lines as the list for the loop:

loop: ‘{{ known.stdout_lines }}’

The second instance is the desired behavior. The difference is subtle at first glance but makes a world of difference in execution.

Walter

Here is an illustration of the difference for those interested.

  • name: test loop constructs

hosts: localhost

gather_facts: false

become: false

vars:

my_list: [ one, two, three ]

tasks:

  • name: construct 1

debug: var=item

loop:

  • ‘{{ my_list }}’

  • name: construct 2

debug: var=item

loop: ‘{{ my_list }}’

% ansible-playbook -i localhost, loop.yml

PLAY [test loop constructs] ********************************************************************************************

TASK [construct 1] *****************************************************************************************************

ok: [localhost] => (item=[‘one’, ‘two’, ‘three’]) => {

“ansible_loop_var”: “item”,

“item”: [

“one”,

“two”,

“three”

]

}

TASK [construct 2] *****************************************************************************************************

ok: [localhost] => (item=one) => {

“ansible_loop_var”: “item”,

“item”: “one”

}

ok: [localhost] => (item=two) => {

“ansible_loop_var”: “item”,

“item”: “two”

}

ok: [localhost] => (item=three) => {

“ansible_loop_var”: “item”,

“item”: “three”

}

PLAY RECAP *************************************************************************************************************

localhost : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0

Walter