Create variable as dict using jinja2 within playbook

Hello there,

Is it possible to create a variable which is a dict using jinja2 within a playbook?

For example, I have the following (contrived) playbook, within which I am trying to create a ‘host_info’ dict which I can then access from a task:

Hi Anth,

Unfortunately, this cannot be done the way you’re trying it. You’re creating YAML after the YAML structure is already parsed, so the variable is just storing it as a string.

Instead of doing this, I would recommend using the add_host module (http://docs.ansible.com/add_host_module.html), which can also accept any additional variables you may wish to associate with that host entry.

Thanks!

I realize this is an old thread, but why does this not work? It works as expected for arrays…

For instance see this blog post:
http://adamj.eu/tech/2014/10/02/merging-groups-and-hostvars-in-ansible-variables/

That works just fine, but if you try to modify this to output an array of dictionaries, or similar, ansible doesn’t interpret the result correctly. Why?

Why is it possible to do with an array though?

  • hosts: all
    connection: local
    gather_facts: false
    tasks:
  • set_fact:
    myvar: |
    {% set comma = joiner(“,”) %}
    [ {% for item in [‘honey’, ‘bunch’] -%}
    {{ comma() }}“monkey”
    {%- endfor %}]
  • debug: var=myvar

PLAY [all] ********************************************************************

TASK: [set_fact ] *************************************************************
ok: [127.0.0.1]

TASK: [debug var=myvar] *******************************************************
ok: [127.0.0.1] => {
“var”: {
“myvar”: [
“monkey”,
“monkey”
]
}
}

PLAY RECAP ********************************************************************
127.0.0.1 : ok=2 changed=0 unreachable=0 failed=0

But:

  • hosts: all
    connection: local
    gather_facts: false
    tasks:
  • set_fact:
    myvar: |
    {% set comma = joiner(“,”) %}
    [ {% for item in [‘honey’, ‘bunch’] -%}
    {{ comma() }}{ monkey: banana }
    {%- endfor %}]
  • debug: var=myvar

Produces:

TASK: [debug var=myvar] *******************************************************
ok: [127.0.0.1] => {
“var”: {
“myvar”: “[ { monkey: banana },{ monkey: banana }]”
}
}

And:

  • hosts: all
    connection: local
    gather_facts: false
    tasks:
  • set_fact:
    myvar: |
    {{ “\n” }}
    {%- for item in [‘honey’, ‘bunch’] -%}
  • { monkey: banana }
    {% endfor %}
  • debug: var=myvar

TASK: [debug var=myvar] *******************************************************
ok: [127.0.0.1] => {
“var”: {
“myvar”: “\n- { monkey: banana }\n- { monkey: banana }\n”
}
}

Why does one way work and the other way fail?

I would also like to know this. I have previously tried to template playbooks using Jinja2, without success. I would like to ask again: wouldn’t it be super if Ansible’s playbooks were first run through Jinja2, so that we could use all of Jinja’s features to customise some parts of playbooks?

Anand

wouldn't it be super if Ansible's playbooks were *first* run through Jinja2, so that
we could use all of Jinja's features to customise some parts of playbooks?

No.

short answer: it defeats the simplicity goal

Sure I understand why not to do this. My question is why does one way work (arrays) and the other way (dicts) not work. Simple question, I can’t find an answer to it.

The answer is that neither returns what you think it does, both return
a string, the issue is that with some fields they expect a string or
array or array in string (which then they split with , to get a real
array). Fields that expect dicts do not do any magic nor expect
strings at any point.

I know this is 2 year old thread, but I bumped into this today and was able to create a dictionary. Something like this:

/tmp $ cat example.yml

  • hosts: 127.0.0.1

vars:
app_servers: 5
ipaddress_base: “192.168.0”
rmi_portbase: 10000
host_info: |
{% set res = -%}
{%- for number in range(1,app_servers + 1) -%}
{% set ignored = res.extend([{
‘hostname’: ‘app’ + number|string,
‘ipaddress’: ipaddress_base + ‘.’ + number|string,
‘rmi_port’: rmi_portbase|int + ( number * 10)
}]) -%}
{%- endfor %}
{{ res }}

tasks:

  • debug: var=host_info
    /tmp $ ansible-playbook example.yml
    [WARNING]: provided hosts list is empty, only localhost is available

PLAY [127.0.0.1] ***************************************************************

TASK [setup] *******************************************************************
ok: [127.0.0.1]

TASK [debug] *******************************************************************
ok: [127.0.0.1] => {
“host_info”: [
{
“hostname”: “app1”,
“ipaddress”: “192.168.0.1”,
“rmi_port”: 10010
},
{
“hostname”: “app2”,
“ipaddress”: “192.168.0.2”,
“rmi_port”: 10020
},
{
“hostname”: “app3”,
“ipaddress”: “192.168.0.3”,
“rmi_port”: 10030
},
{
“hostname”: “app4”,
“ipaddress”: “192.168.0.4”,
“rmi_port”: 10040
},
{
“hostname”: “app5”,
“ipaddress”: “192.168.0.5”,
“rmi_port”: 10050
}
]
}

PLAY RECAP *********************************************************************
127.0.0.1 : ok=2 changed=0 unreachable=0 failed=0