Bookmark this page

Guided Exercise: Implementing Advanced Loops

  • Write tasks that use advanced looping techniques that illustrate some important use cases for complex loops.

Outcomes

  • Use filters and lookup plug-ins to iterate over complex data structures.

As the student user on the workstation machine, use the lab command to prepare your system for this exercise.

This command prepares an Ansible project in the /home/student/data-loops/ directory on the workstation machine.

[student@workstation ~]$ lab start data-loops

Procedure 7.3. Instructions

  1. Use a loop with the dict2items filter to transform the rgroups variable into a list.

    1. Review the structure of the rgroups variable section defined in the ~/data-loops/group_vars/all/my_vars.yml file.

      ### Group variables ###
      rgroups:                  1
        accounts:               2
          name: accounts        3
          gid: 7001
        development:
          name: development
          gid: 7002
      ...output omitted...

      1

      The rgroups variable is a dictionary.

      2

      Each key in the dictionary represents a group name, and the value for each key is another dictionary containing metadata for that group.

      3

      Each group metadata dictionary contains two keys: name and gid.

    2. Review the ~/data-loops/add-groups.yml playbook. This playbook adds a group to the targeted host.

      - name: First task
        hosts: servera.lab.example.com
        gather_facts: false
        become: true
      
        tasks:
          - name: Add groups
            ansible.builtin.group:
              name: accounts
              gid: 7001
              state: present
    3. Modify the playbook to use the loop keyword with the dict2items filter for your loop.

      The add-groups.yml playbook should consist of the following content:

      ---
      - name: First task
        hosts: servera.lab.example.com
        gather_facts: false
        become: true
      
        tasks:
          - name: Add groups
            ansible.builtin.group:
              name: "{{ item['key'] }}"
              gid: "{{ item['value']['gid'] }}"
              state: present
            loop: "{{ rgroups | dict2items }}"
    4. Run the add-groups.yml playbook to add the groups.

      [student@workstation ~]$ cd ~/data-loops/
      [student@workstation data-loops]$ ansible-navigator run -m stdout add-groups.yml
      
      PLAY [First task] *******************************************************
      
      TASK [Add groups] *******************************************************
      changed: [servera...] => (item={'key': 'accounts'....)
      changed: [servera...] => (item={'key': 'development'....)
      ...output omitted...
      
      PLAY RECAP ***************************************************************
      servera...: ok=1 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
  2. Use a nested loop with the subelements filter to transform the my_users and my_groups variables into a list.

    1. Review the structure of the my_users variable section defined in the ~/data-loops/group_vars/all/my_vars.yml file.

      ### User variables ###
      my_users:                  1
        john:                    2
          name: john
          my_groups:             3
            - accounts
            - development
        jane:
          name: jane
          my_groups:
            - manufacturing
            - marketing
      ...output omitted...

      1

      The my_users variable is a dictionary.

      2

      Each key in the dictionary is itself a dictionary, with keys of name and my_groups.

      3

      The my_groups key references a list of groups. For example, the user john becomes a member of the accounts and development groups.

    2. Review the ~/data-loops/add-users.yml playbook. This playbook adds a user and puts it in the correct group.

      - name: Second task
        hosts: servera.lab.example.com
        gather_facts: false
        become: true
      
        tasks:
          - name: Add users and put them in the right groups
            ansible.builtin.user:
              name: john
              append: true
              groups: accounts
              state: present
    3. Modify the playbook to use the loop keyword with the subelements filter for your loop.

      The add-users.yml playbook should consist of the following content:

      ---
      - name: Second task
        hosts: servera.lab.example.com
        gather_facts: false
        become: true
      
        tasks:
          - name: Add users and put them in the right groups
            ansible.builtin.user:
              name: "{{ item[0]['name'] }}"
              append: true
              groups: "{{ item[1] }}"
              state: present
            loop: "{{ my_users | subelements('my_groups') }}"
    4. Run the add-users.yml playbook to add the users.

      [student@workstation data-loops]$ ansible-navigator run -m stdout add-users.yml
      
      PLAY [Second task] *******************************************************
      
      TASK [Add users and put them in the right groups] ************************
      changed: [servera...] => (item=[{'name': 'john', 'my_groups': ['accounts', ...)
      ok: [servera...] => (item=[{'name': 'john', 'my_groups': ['accounts', ...)
      ...output omitted...
      
      PLAY RECAP ***************************************************************
      servera...: ok=1 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
  3. Use a loop over the public_keys_lists variable defined in the group_vars/all/my_vars.yml file. Use the map filter, followed by the flatten filter, to generate a simple list of SSH public key files for all developers. Update the task's key keyword to contain the file content of each iteration item.

    1. Review the structure of the public_keys_lists variable section defined in the ~/data-loops/group_vars/all/my_vars.yml file.

      ### Public key variables ###
      public_key_lists:         1
        - username: john        2
          public_keys:          3
            - pubkeys/john/id_rsa.pub
            - pubkeys/john/laptop_rsa.pub
        - username: jane
          public_keys:
            - pubkeys/jane/id_rsa.pub
      ...output omitted...

      1

      The public_key_lists variable is a list.

      2

      Each entry in the list is a dictionary with the public_keys and username keys.

      3

      The public_keys key references a list of public key files for the given user. For example, the johnd user has two public key files: pubkeys/johnd/id_rsa.pub and pubkeys/johnd/laptop_rsa.pub. The file names are relative to the root directory of the playbook project.

    2. Review the ~/data-loops/add-keys.yml playbook. This playbook sets up authorized keys for the developer user.

      - name: Third task
        hosts: servera.lab.example.com
        gather_facts: false
        become: true
      
        tasks:
          - name: Set up authorized keys
            ansible.posix.authorized_key:
              user: developer
              state: present
              key: pubkeys/john/id_rsa.pub
    3. Modify the playbook to perform the following tasks:

      • Add a map filter to the end of the expression to extract the public_keys attribute. It creates a list of lists; each entry in the list is a list of SSH public key files for a particular user.

      • Add a flatten filter after the map filter to create a single list of files.

      • Use the file lookup plug-in to configure the key keyword with the file content of each iteration item.

      The add-keys.yml playbook should consist of the following content:

      - name: Third task
        hosts: servera.lab.example.com
        gather_facts: false
        become: true
      
        tasks:
          - name: Set up authorized keys
            ansible.posix.authorized_key:
              user: developer
              state: present
              key: "{{ lookup('ansible.builtin.file', item) }}"
            loop: "{{ public_key_lists | map(attribute='public_keys') | flatten }}"
  4. Install the ansible.posix collection so that it is available to the execution environment. This collection is needed for the ansible.posix.authorized_key module.

    1. On the workstation machine, open a browser and navigate to hub.lab.example.com.

    2. Log in as the student user using redhat123 as the password.

    3. Navigate to CollectionsAPI token management and then click Load Token.

    4. Copy and paste the token in the token variable at the end of the ~/data-loops/ansible.cfg file.

    5. Install the collection:

      [student@workstation data-loops]$ ansible-galaxy collection \
      > install -r collections/requirements.yml -p collections/
      ...output omitted...
  5. Run the add-keys.yml playbook to add the keys.

    [student@workstation data-loops]$ ansible-navigator run -m stdout add-keys.yml
    
    PLAY [Third task] ********************************************************
    
    TASK [Set up authorized keys] ********************************************
    changed: [servera.lab.example.com] => (item=pubkeys/john/id_rsa.pub)
    changed: [servera.lab.example.com] => (item=pubkeys/john/laptop_rsa.pub)
    changed: [servera.lab.example.com] => (item=pubkeys/jane/id_rsa.pub)
    ...output omitted...
    
    PLAY RECAP ***************************************************************
    servera...: ok=1 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0

Finish

On the workstation machine, change to the student user home directory and use the lab command to complete this exercise. This step is important to ensure that resources from previous exercises do not impact upcoming exercises.

[student@workstation ~]$ lab finish data-loops

This concludes the section.

Revision: do374-2.2-82dc0d7