Fun with Ansible variable interpolation


After losing some hair over the question how does Ansible do hiera-style %{} variable interpolation? I figured I'd jot down my findings. If nothing else, it'll serve as a handy cheatsheet for future me.

Background

In Hiera one can do variable interpolation and internal lookups. So that's Hiera calls withing Hiera. Very handy to reduce deplication as it helps to compose variables out of values from other layers in your hierarchy. For example on an office level you can define your gateway, and on a node-level you can re-use the value of the gateway in your per-interface config:

office.yaml:

gateway: 10.1.0.254

and in your servera.yaml:

interfaces:
  em0:
    gatway: "%{hiera('office::gateway')}"

You get the idea.

Ansible

Ansible doesn't use Hiera of course, but it knows of a similar method to allow for defining variables in 42 places (at least it feels like that many). For our example we'll simply use group_vars and host_vars where the latter override the former.

Our office.yaml can stay the same, and our servera.yaml now looks like:

interfaces:
  em0:
    gatway: "{{ gateway }}"

As you can see it's Jinja templating taking over again. And of course you can compose at will (switching to meaningless variable names and values):

var1: 3
var2: [1,2, "{{ var1 }}"]

I'll using a sample task like this to show the values:

    - debug: var="{{ item }}"
      with_items:
        - var1
        - var2

Which results in:

ok: [cuzco] => (item=var1) => {
    "item": "var1",
    "var1": 3
}
ok: [cuzco] => (item=var2) => {
    "item": "var2",
    "var2": [
        1,
        2,
        3
    ]
}

We can expand the following by using a variable as a dictionary (hash) value:

value: "value1"
var3:
  key1: "{{ value }}"

Which results in:

ok: [cuzco] => (item=var3) => {
    "item": "var3",
    "var3": {
        "key1": "value1"
    }
}

So far so good. However using a variable for a dictionary key requires a syntax that's slightly less obvious:

key: key2
var4: "{
  '{{ key }}': value2
}"

By quoting the literal dictionary syntax we can then use single quotes around the key name to force a lookup:

ok: [cuzco] => (item=var4) => {
    "item": "var4",
    "var4": "{ 'key2': value2 }"
}

Hurray!

And of course we can combine variable keys and values now:

value: value1
key: key2
var5: "{
  key1: {{ value }},
  '{{ key }}': value2
}"

resulting in:

ok: [cuzco] => (item=var5) => {
    "item": "var5",
    "var5": "{ key1: value1, 'key2': value2 }"
}

The normal Ansible variable precedence is in effect here meaning that the variable key can be defined as a group variable and re-used in the host variable definitions.