Create Dynamic List in Ansible with Jinja – How to

ansibleansible-playbookjinjapython

I need to dynamically set a new list type variable list var.

Here's a basic playbook example:

  vars:
    app_instances:
      - host_name: host1-domain
        inst_count: 3
      - host_name: host2-domain
        inst_count: 1
      - host_name: host3-domain
        inst_count: 1

  tasks:
    - set_fact:
        instance_config: >-
          {% set inst_config = [] %}
          {% for inst in app_instances %}
            {% for inst_num in range(inst.inst_count) %}
              {% set node_number = inst.host_name.split('-') | first | replace('host', '') %}
              {% set host_name = "host_name" %}
              {% set host_num = "host_num" %}
              {% set inst_name = "inst_name" %}
              {% set node_conf = { host_name: inst.host_name, host_num: node_number, inst_name: inst_num+1 } %}
              {{ inst_config.append(node_conf) }}
            {% endfor %}
          {% endfor %}
          {{ inst_config|join(",") }}

    - debug:
        msg: "{{ instance_config }}"

This obviously sets the instance_config to a string with the following contents:

"instance_config": "                           \n                          \n                          \n                            \n                            \n   {'host_name': 'host1-domain', 'host_num': '1', 'inst_name': 1},{'host_name': 'host1-domain', 'host_num': '1', 'inst_name': 2},{'host_name': 'host1-domain', 'host_num': '1', 'inst_name': 3},{'host_name': 'host2-domain', 'host_num': '2', 'inst_name': 1},{'host_name': 'host3-domain', 'host_num': '3', 'inst_name': 1}"

So while the structure of the list that I'm getting is correct, it's a String and I can't seem to make this into a variable that would be a list instead.
What Am I missing here? What I need eventually is a variable:

instance_config = [
  {'host_name': 'host1-domain', 'host_num': '1', 'inst_name': 1},
  {'host_name': 'host1-domain', 'host_num': '1', 'inst_name': 2},
  {'host_name': 'host1-domain', 'host_num': '1', 'inst_name': 3},
  {'host_name': 'host2-domain', 'host_num': '2', 'inst_name': 1},
  {'host_name': 'host3-domain', 'host_num': '3', 'inst_name': 1}
]

Best Answer

Answering my own question as I have found a solution:

  1. enable do jinja extension in ansible.cfg: jinja2_extensions = jinja2.ext.do
  2. replace {{ inst_config.append(node_conf) }} with {% do inst_config.append(node_conf) %}
  3. trim whitespaces using {%- and -%}.

The final result of the set_fact task looks like this:

    - set_fact:
        instance_config: >-
          {%- set instance_config = [] -%}
          {%- for inst in app_instances -%}
            {%- for inst_num in range(inst.inst_count) -%}
              {%- set node_number = inst.host_name.split('-') | first | replace('host', '') | int -%}
              {%- set host_name = "host_name" -%}
              {%- set host_num = "host_num" -%}
              {%- set inst_name = "inst_name" -%}
              {%- set node_conf = { host_name: inst.host_name, host_num: node_number, inst_name: inst_num+1 } -%}
              {%- do instance_config.append(node_conf) -%}
            {%- endfor -%}
          {%- endfor -%}
          {{ instance_config }}