'Ansible playbook loop only calling final loop

I'm trying to create a playbook that will loop based on the number of devices, creating a different file for each, and subsequently each then getting a hostname within that matches that of the filename.

It looks like the loop caches the 2 filenames, but doesn't create, and then later has forgotten the frist in the loop, remembers its attributes but uses the intended filename of the second in the loop.

Been at this a while, am I missing something obvious?

playbooks/test.yaml

- name: Create Folder Structure
  hosts: switches
  gather_facts: false
  vars:
    project_name: test
    switch_stacks:
      - id: 01
        installation_floor: 1
      - id: 02
        installation_floor: 2
    device_name: swi
    site_abbrev: lon

  tasks:
    - name: Create Site Specific Folder
      file:
        path: /home/usr/complete_config/{{ project_name }}
        state: directory
        mode: 0755

    - name: Set Destination Directory & Filename for Switch Configurations
      set_fact:
        dest_dir: /home/usr/complete_config/{{ project_name }}
        switch_device_name: '{{ device_name|lower }}-{{ site_abbrev|lower }}-{{ item.installation_floor }}{{ "%02d" | format(item.id) }}'
      loop: "{{ switch_stacks }}"

- name: Create Switch Configurations
  hosts: switches
  gather_facts: false

  tasks:
    - name: Generate Switch Configurations
      template:
        src: /home/usr/templates/switch-template.j2
        dest: "{{ dest_dir }}/{{ switch_device_name }}"
        lstrip_blocks: yes
      delegate_to: localhost

See the folder structure before:

usr@ans:~$ tree complete_config/test
complete_config/test [error opening dir]

0 directories, 0 files

Running of the playbook:

usr@ans:~$ ansible-playbook playbooks/test.yaml

PLAY [Create Folder Structure] **************************************************************************************

TASK [Create Site Specific Folder] **********************************************************************************
changed: [switch]

TASK [Set Destination Directory & Filename for Switch Configurations] ***********************************************
ok: [switch] => (item={'id': 1, 'installation_floor': 1})
ok: [switch] => (item={'id': 2, 'installation_floor': 2})

PLAY [Create Switch Configurations] *********************************************************************************

TASK [Generate Switch Configurations] *******************************************************************************
changed: [switch -> localhost]

PLAY RECAP **********************************************************************************************************
switch              : ok=3    changed=2    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

The folder structure afterwards:

usr@ans:~$ tree complete_config/test/
complete_config/test/
└── swi-lon-202

0 directories, 1 file


Solution 1:[1]

You're using set_fact wrongly. Since you override the variables you set on every iteration only the last one remains. What you want to do is:

  • get rid of your set_fact that is useless here
  • get rid of the second play and move the task at the end of the first changing it like so (written on spot and untested)
    - name: Generate Switch Configurations
      vars:
        dest_dir: /home/usr/complete_config/{{ project_name }}
        switch_device_name: '{{ device_name|lower }}-{{ site_abbrev|lower }}-{{ item.installation_floor }}{{ "%02d" | format(item.id) }}'
      template:
        src: /home/usr/templates/switch-template.j2
        dest: "{{ dest_dir }}/{{ switch_device_name }}"
        lstrip_blocks: yes
      delegate_to: localhost
      loop: "{{ switch_stacks }}"

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Zeitounator