Using registered variable in a loop

I’m trying to configure Molecule to use Azure’s Devtest Lab virtual machines for testing playbooks using the delegated|default driver. The current issue that I’m running into is getting the necessary data from a registered variable from the azure_rm_devtestlabvirtualmachine_info module that is being used in a loop to populate the instances-config.yml file.

Below is a snippet of the section in question. If necessary, the entire create.yml playbook can be provided. Please note that there is not a problem with the virtual machines being created.

    - name: Get instance information to populate instance-config file
      azure.azcollection.azure_rm_devtestlabvirtualmachine_info:
        lab_name: "{{ molecule_yml.driver.azure_lab_name }}"
        resource_group: "{{ molecule_yml.driver.azure_resource_group }}"
        name: "{{ item.name }}"
      register: server
      changed_when: true # Task is always changed.
      loop: "{{ molecule_yml.platforms }}"

    - name: Display registered variable contents
      ansible.builtin.debug:
        var: server.results | selectattr('virtualmachines') | length

    - name: Create instance config
      when: server.changed | default(false) | bool  # noqa no-handler
      block:
        - name: Populate instance config dict  # noqa jinja
          ansible.builtin.set_fact:
            instance_conf_dict: {
              'instance': "{{ item.name }}",
              'address': "{{ item.fqdn }}",
              'user': "{{ item.user_name }}",
              'identity_file': "{{ molecule_yml.driver.ssh_key_file }}",
              'port': "22"
            }
          loop: "{{ server.results | selectattr('virtualmachines') | list }}"
          register: instance_config_dict

The name, fqdn, and user_name return as either not being defined or not being an attribute in a list or dictionary. Maybe there’s a different way that the item variable can be noted.

Here is a shortened version of the variable in question. The specific section of the result I’m trying to access is the virtualmachines section, which appears to be a list. Using server.results.virtualmachines produces a VARIABLE IS NOT DEFINED error. server.results | selectattr('virtualmachines', 'defined') | map(attribute='virtualmachines') and server.results | selectattr('virtualmachines', 'defined') produce output, but it looks the same as the original.

[localhost] => {
    "server": {
        "changed": true,
        "msg": "All items completed",
        "results": [
            {
                "ansible_loop_var": "item",
                "changed": true,
                "failed": false,
                "invocation": {
                    "module_args": {
                     ...
                    }
                },
                "item": {
                ...
                },
                "virtualmachines": [
                    {
                        ...
                        "fqdn": "VM-NAME.example.com",
                        ...
                        "image": {
                        ...
                        },
                        ...
                        "name": "VM-NAME",
                        "notes": "Virtual machine notes, just something...",
                        "os_type": "linux",
                        "provisioning_state": "Succeeded",
            }
        ],
        "skipped": false
    }
}

In case this information is relevant.

molecule 24.2.0 using python 3.12 
    ansible:2.16.4
    default:24.2.0 from molecule

I’ve probably missed something very basic when working with more complex data structures.

selectattr() filters a list by testing whether it meets the criterion (either a test name like 'defined' or if no test is provided it checks for truthiness.) It does not change the individual list entries in any way, so in this case it will probably still be the exact same list.

You want something more like this:

loop: "{{ server.results | map(attribute='virtualmachines') | flatten }}"
2 Likes

Fantastic! That did the trick. Thank you so much! I’ve been trying to wrap my head around this all day.

Could you please explain more about why this solution worked? More specifically regarding the map filter. I’ve read Manipulating data and the Jinja2 documentation for it, and it appears that more than attribute can be passed to it, filters for example. Are there even more options that it uses? If you have any other sources that have helped learning about using filters, I would greatly appreciate it.