23

I'm using Ansible with Jinja2 templates, and this is a scenario that I can't find a solution for in Ansible's documentation or googling around for Jinja2 examples. Here's the logic that I want to achieve in Ansible:

if {{ existing_ansible_var }} == "string1"
  new_ansible_var = "a"
else if {{ existing_ansible_var }} == "string2"
  new_ansible_var = "b"
<...>
else
  new_ansible_var = ""

I could probably do this by combining several techniques, the variable assignment from here: Set variable in jinja, the conditional comparison here: http://jinja.pocoo.org/docs/dev/templates/#if-expression, and the defaulting filter here: https://docs.ansible.com/playbooks_filters.html#defaulting-undefined-variables ,

...but I feel like that's overkill. Is there a simpler way to do this?

Community
  • 1
  • 1
s g
  • 5,289
  • 10
  • 49
  • 82
  • What are you trying to achieve? – Antonis Christofides Jun 04 '15 at 08:17
  • I'm trying to set `new_ansible_var` based on several conditions so that I can use `new_ansible_var` in a .j2 template – s g Jun 04 '15 at 16:15
  • I got that, but why do you want this? If you explain what your actual problem is, we may be able to help you do it in a better way. – Antonis Christofides Jun 04 '15 at 18:35
  • 1
    Please see my updated answer below. I added an example for defining a var beside simply writing the value out. – udondan Jun 04 '15 at 19:38
  • @AntonisChristofides - in my `.j2` file, I have a line that I want to look like this: `tags: {{ perm_tags }} {{ optional_tags}}`. The `optional_tags` should have a value if and only if `{{ machine_type }}` matches one of many different strings. The string comparison set may grow in the future, or there may be other variables besides `machine_type`. Ideally, my .j2 tag would be even more generalized so the line reads `tags: {{ my_tags }}` and `my_tags` would be defined elsewhere (i.e. in a vars file?) as `{{ perm_tags }}` concatenated with `{{ optional_tags }}` – s g Jun 04 '15 at 19:50
  • @AntonisChristofides my previous comment was from a jinja2 point of view. From the Ansible point of view - I'm deploying different types of machines, each of which has a config file. The config file is nearly identical for all the machines except for the `tags: ` line which is machine dependent, and the 'stuff' uses variables which all exist in Ansible. Some are custom variables, and others are magic variables like `inventory_hostname` – s g Jun 04 '15 at 23:36

3 Answers3

46

If you just want to output a value in your template depending on the value of existing_ansible_var you simply could use a dict and feed it with existing_ansible_var.

{{ {"string1": "a", "string2": "b"}[existing_ansible_var] | default("") }}

You can define a new variable the same way:

{% set new_ansible_var = {"string1": "a", "string2": "b"}[existing_ansible_var] | default("") -%}

In case existing_ansible_var might not necessarily be defined, you need to catch this with a default() which does not exist in your dict:

{"string1": "a", "string2": "b"}[existing_ansible_var | default("this key does not exist in the dict")] | default("")

You as well can define it in the playbook and later then use new_ansible_var in the template:

vars: 
   myDict:
     string1: a
     string2: b
   new_ansible_var: '{{myDict[existing_ansible_var | default("this key does not exist in the dict")] | default("") }}'
udondan
  • 57,263
  • 20
  • 190
  • 175
  • This is a great solution! Now I'm wondering how to do it in Ansible without using Jinja... – s g Jun 08 '15 at 17:13
  • ... or if I can use the same syntax/construct in a playbook and be able to access `new_ansible_var` in the same playbook – s g Jun 08 '15 at 19:45
  • 2
    Yes, that's possible too. I added an example to my answer above. – udondan Jun 08 '15 at 20:13
  • If you need this while writing a role, you can put the required dictionary into a variable in the role vars file `vars/main.yml`. Often this is easier to read than a single line with the dictionary data and logic, and the code in the role becomes more self-explanatory. – RichVel Jan 19 '17 at 14:28
6

Something like this would work, but it's ugly. And as @podarok mentioned in his answer, it's likely unnecessary depending on exactly what you're attempting to do:

- name: set default
  set_fact: new_ansible_var= ""

- name: set to 'a'
  set_fact: new_ansible_var= "a"
  when: "{{ existing_ansible_var }} == string1"

- name: set to 'b'
  set_fact: new_ansible_var= "b"
  when: "{{ existing_ansible_var }} == string2"

etc.

Bruce P
  • 19,995
  • 8
  • 63
  • 73
  • Line 2 of these examples should look like this: `set_fact: new_ansible_var= "a"` to be able to run it. I made that change and now when I run the play I get the following error which is very unhelpful: `TASK: [sbc | set to 'a'] ***************************************************** fatal: [my-machine] => error while evaluating conditional: existing_ansible_var == string1 FATAL: all hosts have already failed -- aborting` – s g Jun 08 '15 at 19:54
  • I also had to make the 3rd line look like: `when: "{{ existing_ansible_var }} == string1"` – s g Jun 08 '15 at 20:01
0

you don't need to set var, because I'm guessing that you trying to set var for some condition later. Just make condition there like

- name: Later task
  shell: "command is here"
  when: {{ existing_ansible_var }} == "string1"

and get a profit

podarok
  • 537
  • 5
  • 12