Merging groups and hostvars in Ansible variables

Tools of the trade.

I was googling for information on creating round-robin DNS records in Route53 with Ansible and came across this post by Daniel Hall who’d figured out a way of merging the his host group list with their hostvars in Ansible. After a bit of emailing with him I’ve figured a way to do it without having to add an extra filter as he did originally - by digging more into Jinja2.

What’s the problem?

1. A host group in ansible is a list of their addresses - e.g. ["", ""]

2. hostvars is a dictionary of all the known ‘facts’ for each system, e.g.:

    "": {
        "ansible_default_ipv4": {
            "address": ""

3. Given a host group, we want to create a single string that is the comma-separated concatenation of all of their ip4 addresses from hostvars.

It uses a few of the lesser-used jinja2 features which I’ll talk through.

Here’s the example playbook (test.yml):

- hosts: all
  connection: local
    - debug:
        # Comma-separated string of all ip4 addresses of hosts
        msg: |
          {% set comma = joiner(",") %}
          {% for item in ['', ''] -%}
              {{ comma() }}{{ hostvars[item].ansible_default_ipv4.address }}
          {%- endfor %}

You can test it by running with:

ansible-playbook test.yml -i,

Yes, the way you test Ansible without an inventory file is a comma-separated list of hosts - but the comma is needed for just one host.

It gives the output (cows disabled for conciseness):

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

GATHERING FACTS ***************************************************************
ok: []

TASK: [debug ] ****************************************************************
ok: [] => {
    "msg": ","

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


How does it work?

First thing to notice is the inner loop - {% for item in .... I’m looping on an example array but you can easily replace this with a hosts list. At the end of the loop declaration, a dash is added before the end of the tag - -%} - this means jinja2 will collapse any whitespace it encounters in the loop.

Secondly, the ipv4 address is output with a simple lookup: hostvars[item].ansible_default_ipv4.address. There are a lot of facts that ansible gathers; add the task debug: var=hostvars to the playbook to see all variables available.

Thirdly, there’s this joiner thing being used. This is a template function in Jinja2 precisely for the purpose of joining strings with separators. It outputs the string you pass it on its 2nd+ calls. It’s better documented on Jinja2 itself.

Anyway, this was fun… especially since I didn’t need to implement round-robin DNS myself in the end! Also, there is maybe an easier way and I don’t know it. If you know, please tell me.

Make your development more pleasant with Boost Your Django DX.

Subscribe via RSS, Twitter, or email:

One summary email a week, no spam, I pinky promise.