Search code examples
ansiblejinja2

Include variables into a playbook from an in-place rendered jinja2 template without intermediate file


I need to create some variables, lists, etc that are dynamically generated from other variables -- think of a list of nodes for kubernetes built with for loop.

This can be done inside Ansible with the set_fact and looping through incrementally with a default(), adding each list item each loop, but I don't want to build up all these vars with tasks.

I want to use a template to build the list and then use the result as variables. This works fine if I render the template to a file, and then load it into the playbook with vars_files or include_vars, but I wonder if it is possible to skip the intermediate file and do an in-place render-template realize-variables?

rendered_template.j2

nodes:
{% for item in nodes_struct %}
  - name: "{{ item.name }}"
    ipaddress: "{{ item.ipaddress }}"
    networkifacename1: "enp1s0"
    networkifacename2: "enp2s0"
{% endfor %}

playbook.yaml

- hosts: all
  vars_files:
    - ../vars/rendered_template.yaml

or

- name: Render global variables
  include_vars: "../vars/rendered_template.yaml"

The above is just a very simple example from a snippet inside the template. Most of the solutions suggest building up the vars instead with playbook tasks using the map function or nodes: | construct. I was trying to avoid doing ~60 playbook tasks instead of 60 vars rendered from the template in-place, but maybe that's the only way.

Would have been nice if I could use something like

- name: show templating results
  set_fact:
    template_var: "{{ lookup('template', '../vars/rendered_template.j2') }}"

And then somehow flatten it into var space from the root of that rendered template var instead of getting to it like. code below does not work)

- debug:
    msg: "{{ template_var.nodes }}"

Solution

  • Ok, I went elsewhere and coaxed the answer to my Q. This code works for my purpose and is concise.

    - set_fact:
        template_var: "{{ lookup('template', '../vars/rendered_template.j2') }}"
    
    - set_fact:
        template_var_yaml: "{{ template_var | from_yaml }}"
    
    - name: Convert to top-level
      set_fact:
        "{{ item.key }}": "{{ item.value }}"
      loop: "{{ template_var_yaml|dict2items }}"
      loop_control:
        label: "{{ item.key }}"
    
    - debug:
        var: nodes
    

    Of course, it can be reduced down to

    - name: Convert to top-level
      set_fact:
        "{{ item.key }}": "{{ item.value }}"
      loop: "{{ lookup('template', '../vars/rendered_template.j2')|from_yaml|dict2items }}"
      loop_control:
        label: "{{ item.key }}"
    
    - debug:
        var: nodes
    

    Leaving the vars "namespaced" from inside template_var.nodes would also be useful so as not to pollute the top-level var space, but in my case, the source template variables were used extensively later by a bunch of roles I'm using that someone else made, so I need to keep them top-level.