36

Depending on the value(True/False) of a variable defined into the group_vars I am trying to define some variables in a vars file. Their value depends on the group var's value.

My current var file looks like this:

{% if my_group_var %}
test:
   var1: value
   var2: value
   ...
   varn: value
{% else %}
test:
   var1: other_value
   var2: other_value
   ...
   varn: other_value
{% endif %}

For each one of my roles I'm using a variable defined into this file.

My test playbook looks like below:

- name: blabla
  hosts: blabla
  vars_files:
     - <path>/test_vars.yml
  roles: blabla 

The error I'm receiving after running the playbook is:

{% if my_group_var %}
 ^ here

exception type: <class 'yaml.scanner.ScannerError'>
exception: while scanning for the next token
found character that cannot start any token
  in "<unicode string>"

Am I doing something stupid here or this is not even supported? I've tried to find another way for defining these vars(I have a lot of them) but I didn't managed to get something functional here. Any suggestions?

3
  • Where do these vars end up being used? This might be unnecessary if all you're going to use them for is part of a template module call.
    – 84104
    Apr 11, 2018 at 15:21
  • If test is group dependent, it should be placed into group_vars. Apr 11, 2018 at 15:25
  • Unfortunately, test is not group dependent. As mentioned in the description, test is depending on the value of a group_var variable.
    – pandoJohn
    Apr 12, 2018 at 4:51

9 Answers 9

36

Ansible allows one of following forms to define variable conditionally:

    test:
      var1: "{% if my_group_var %}value{% else %}other_value{% endif %}"
      var2: "{{'value' if (my_group_var) else 'other_value'}}"

Combining above syntax with vars lookup we can load complex vars (map in this case):

test_value_when_my_group_var_is_true:
   var1: value
   var2: value

test_value_when_my_group_var_is_false:
   var1: other_value
   var2: other_value

test: "{{ lookup('vars','test_value_when_my_group_var_is_true') if (my_group_var) else lookup('vars','test_value_when_my_group_var_is_false')}}"

There is another way of doing conditional tree loading with vars lookup. This way is handy when you need implement case logic (i.e. condition variable has more than two possible values):

test_value_when_my_group_var_is_foo:
   var1: value
   var2: value

test_value_when_my_group_var_is_bar:
   var1: other_value
   var2: other_value

test_value_when_my_group_var_is_baz:
   var1: yet_another_value
   var2: yet_another_value

test: "{{ lookup('vars','test_value_when_my_group_var_is_' + my_group_var) }}"
1
  • That last method of conditional logic worked perfectly for me. Thanks for the solution!
    – Stan
    Jan 12, 2022 at 21:04
17

I don't think you can, I usually create separate files to hold conditional variable collections and use a when clause to include them on a specific condition:

- include_vars: test_environment_vars.yml
  when: global_platform == "test"

- include_vars: staging_environment_vars.yml
  when: global_platform == "staging"

- include_vars: prod_environment_vars.yml
  when: 
    - global_platform != "test" 
    - global_platform != "staging" 
4
  • 3
    This is something I was trying to avoid. I don't want to use include vars or to define 2 additional var files for my tasks.
    – pandoJohn
    Apr 11, 2018 at 15:00
  • I don't get it..when it's possible to use include_vars with condition, why not variable can be defined with condition?
    – GP92
    May 3, 2019 at 3:31
  • this is in my eyes the best solution: divide and conquer.
    – TRW
    Jan 5, 2021 at 6:30
  • I am new to ansible and this ans save lot of my effort. Thanks Aug 25, 2021 at 9:54
2

Despite it would be nice, I'm afraid that your way isn't possible (or I'm not aware right way).

I'd suggest to first prepare vars file from jinja template and then include it with include_vars. See the example playbook:

---
- name: -Test-
  hosts: local
  vars:
    my_group_var: False
#    my_group_var: True

  tasks:

  - name: Prepare vars file from template.
    template: src=/tmp/vars.yaml.j2
              dest=/tmp/vars.yaml

  - name: Include vars
    include_vars: "/tmp/vars.yaml"

The content of example jinja template /tmp/vars.yaml.j2 is:

{% if my_group_var %}                                                                                                                                                                                             
test:                                                                                                                                                                                                             
   var1: value                                                                                                                                                                                                    
   var2: value                                                                                                                                                                                                    
{% else %}                                                                                                                                                                                                        
test:                                                                                                                                                                                                             
   var1: other_value                                                                                                                                                                                              
   var2: other_value                                                                                                                                                                                              
{% endif %}
2
  • I like this but the problem is that after generating the .yml file from the jinja template this is not available for the subsequent tasks from my playbook.
    – pandoJohn
    Apr 12, 2018 at 4:58
  • Why? When I tried to extend the test playbook with debug task - debug: var=test I can see, that there are defined both test.var1 and test.var2 with expected values. So you should be able to use these variables in other tasks of the playbook. Apr 12, 2018 at 9:25
2

Why no one mentions this?

_dist: "{{ ansible_lsb.id|lower if ansible_lsb.id == 'Raspbian' else ansible_distribution|lower }}"
1
  • This is the second case in the top voted answer, posted almost three years before you did.
    – Benjamin
    Feb 23 at 14:44
0

AFAIK, this {} thing you're trying is for Jinja2 templates and not for yml files. From the documentation:

ansible allows Jinja2 loops and conditionals in templates, but in playbooks, we do not use them.

You can prepare a separate .yml file with a play that sets up the variables you need using when clauses, and then importing (or including) them in your main playbook. That way you can have all vars conditionally defined in a single file.

OR: Use roles. I think roles are the most adequate approach to solve your problem.

0

What I did in the end was to create 2 separate var files - let's call them type_a.yml and type_b.yml - and I defined a group variable that indicates what file to use - something like type_of_file: a. This is how my playbook looks now:

- name: blabla
  hosts: blabla
  vars_files:
     - <path>/type_{{ type_of_file }}.yml
  roles: blabla

Thanks for all the answers. I find them really helpful.

0

I hope the following snip (taken from Ansible Documentation) may make a worthwhile addition to the useful contributions from others above.

- name: Show value of 'variablename'
  debug: msg="{{ lookup('vars', 'variabl' + myvar)}}"
  vars:
    variablename: hello
    myvar: ename
0

Jinja2 code is expected in '.j2' files for templates, and inline playbooks, where some rules apply. This is why you're getting those syntax erros.

Ansible has a pletora of ways to accomplish the same tasks.

If you want to define variables for each role, one approach is to use a separate var file for each role:

roles/
    some_role/            # this hierarchy represents a "role"
        vars/             #
            main.yml      #  <-- variables associated with this role
        defaults/         #
            main.yml      #  <-- default lower priority variables for this role

The above method will not require conditionals (the 'when' clause).

If you actually want to have one single file with all variable definitions, you'll end up with a template file for generating the final var file and then include/import it into your playbook.

I would strongly recommend the understanding of the concepts of code reuse and dynamic versus static content.

Also, if there are no namespace conflicts or not too many variables, one should consider one single, static global var file for each playbook, or set of playbooks.

As of Ansible Documentation on Using Variables (Variable precedence: Where should I put a variable?):

A lot of folks may ask about how variables override another. Ultimately it’s Ansible’s philosophy that it’s better you know where to put a variable, and then you have to think about it a lot less.

Avoid defining the variable “x” in 47 places and then ask the question “which x gets used”. Why? Because that’s not Ansible’s Zen philosophy of doing things.

There is only one Empire State Building. One Mona Lisa, etc. Figure out where to define a variable, and don’t make it complicated.

There's a great ordered list of variable precedence here.

0

Another clean-ish way to do this is to use in-line jinja templates.

For example:

#!/usr/bin/env ansible-playbook
---
- name: Do some testing
  hosts: localhost
  gather_facts: no
  vars:
    primary_var: "one"
    dependent_var_one: |-
      {%- if primary_var == "one" -%}
      value_one
      {%- elif primary_var == "two" -%}
      value_two
      {%- else -%}
      other_value
      {%- endif -%}
    dependent_var_two: |-
      {%- if dependent_var_one == "value_one" -%}
      sub_value_one
      {%- elif dependent_var_one == "value_two" -%}
      sub_value_two
      {%- else -%}
      sub_other_value
      {%- endif -%}
  tasks:
  - name: Show var one
    ansible.builtin.debug:
      var: dependent_var_one

  - name: Show var two
    ansible.builtin.debug:
      var: dependent_var_two

Will produce

PLAY [Do some testing] *****************************************************************************************************************************************************************************************************************************************

TASK [Show var one] ********************************************************************************************************************************************************************************************************************************************
ok: [localhost] => {
    "dependent_var_one": "value_one"
}

TASK [Show var two] ********************************************************************************************************************************************************************************************************************************************
ok: [localhost] => {
    "dependent_var_two": "sub_value_one"
}

PLAY RECAP *****************************************************************************************************************************************************************************************************************************************************
localhost                  : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .