76

I want to conditionally define a variable in an Ansible playbook like this:

my_var: "{{ 'foo' if my_condition}}"

I would like the variable to remain undefined if the condition does not resolve to true.

Ansible gives the following error if I try to execute the code:

fatal: [foo.local] => {'msg': 'AnsibleUndefinedVariable: One or more undefined
                       variables: the inline if-expression on line 1 evaluated
                       to false and no else section was defined.', 'failed': True}

Why is this an error anyway?

The complete case looks like this:

{role: foo, my_var: "foo"}

If my_var is defined, the role does something special. In some cases, I don't want the role to do this. I could use when: condition, but then I would have to copy the whole role block. I could also use an extra bool variable, but I would like a solution without having to change the "interface" to the role.

Any ideas?

Eddie C.
  • 918
  • 10
  • 16
Christian
  • 4,042
  • 4
  • 26
  • 28
  • Take a look at: [Ansible: Set variable only if undefined](https://stackoverflow.com/a/49653974/658497) – Noam Manos Apr 04 '18 at 14:50
  • According to the Jinja Template Designer Documentation, "The else part is optional. If not provided, the else block implicitly evaluates into an Undefined object (regardless of what undefined in the environment is set to)". I guess for Ansible, Undefined is not allowed and you should use the else as indicated in the accepted answer. Weird thing is that this definitely works without the else in some Ansible installs like my own, but not the one at work. – DaVince Sep 28 '21 at 14:59

5 Answers5

98

You could use something like this:

my_var: "{{ 'foo' if my_condition else '' }}"

The 'else' will happen if condition not match, and in this case will set a empty value for the variable. I think this is a short, readable and elegant solution.

Eddie C.
  • 918
  • 10
  • 16
mhalano
  • 1,102
  • 7
  • 8
  • 7
    And in case my_var should be set to other variables value, which might not been defined, try something like: `{{ var_a if var_a is defined else var_b | default('default value') }}` – Noam Manos Apr 04 '18 at 12:44
  • 1
    can it be set to a dictionary in a similar way? Tried: `my_var: "{{ '{key1: value1, key2: value2 }' if my_condition }}"` without any luck ((( – Drew May 03 '18 at 05:31
  • 7
    OP told you: "I would like the variable to remain undefined" and you answered with setting it to `''` instead. – poige Apr 08 '19 at 16:13
  • @poige Undefined variable in Ansible is a tricky question because Ansible doesn't use a complete programming language. So the best you can do is set the value of the variable to an empty value or doesn't set the variable at all. – mhalano Jun 17 '21 at 12:14
30

This code may help you to define a variable with condition.

- hosts: node1
  gather_facts: yes
  tasks:
   - name: Check File
     shell: ls -ld /etc/postfix/post-install
     register: result
     ignore_errors: yes

   - name: Define Variable
     set_fact:
         exists: "{{ result.stdout }}"
     when: result|success

   - name: Display Variable
     debug: msg="{{ exists }}"
     ignore_errors: yes

So here the exists will display only if the condition is true.

Eddie C.
  • 918
  • 10
  • 16
SPM
  • 714
  • 1
  • 8
  • 17
  • 18
    I'd recommend using the stat module to check for the file rather than using the shell command. – Bruce P Jul 10 '15 at 13:49
20

My example, after https://stackoverflow.com/a/43403229/5025060:

vars:
    sudoGroup: "{{ 'sudo' if ansible_distribution == 'Ubuntu' else 'wheel' }}"

Because of the different sudo conventions used by Ubuntu versus other platforms, here I am telling Ansible to set a variable named sudoGroup to sudo if the platform is Ubuntu, otherwise set it to wheel.

Later in my playbook, I combine the variable with Ansible's user module to add either sudo or wheel to an account's secondary groups depending on the OS Ansible is running on:

- name: Add or update bob account
  user:
    name: bob
    uid: 3205
    groups: "{{ sudoGroup }}"
    append: yes

NOTES:

  • Double quotes around the {{ variable }} are required in the user: groups: definition above.
  • Once I define sudoGroup as above in my playbook's global vars: section, Ansible configures it at run time (based on ansible_distribution) for each target I define in my hosts: section.
CODE-REaD
  • 2,819
  • 3
  • 33
  • 60
17

I believe you're after the default(omit) filter. (Reference).

As per the example, mode will behave like it wasn't set at all for the first two items in the loop.

- name: touch files with an optional mode
  file:
    dest: "{{item.path}}"
    state: touch
    mode: "{{item.mode|default(omit)}}"
  loop:
    - path: /tmp/foo
    - path: /tmp/bar
    - path: /tmp/baz
      mode: "0444"
Eddie C.
  • 918
  • 10
  • 16
LucidObscurity
  • 580
  • 7
  • 10
  • You can also cram pretty much any other value in there (e.g. `foo | default(5)`) [docs](https://docs.ansible.com/ansible/latest/user_guide/playbooks_filters.html#defaulting-undefined-variables). – M. Davis Jun 11 '18 at 14:28
4

This can be set as with bool:

- name: Conditional (true and false)
  set_fact: 
      my_boolean_set_to_be: "{{ 'true' if my_var == 'foo' else 'false' }}"  

- name: Display Variable
  debug: msg="{{ my_boolean_set_to_be }}"

This can be set as for more conditionals like 'if-ifelse-else' statements:

- name: Conditional for 'my_var' (2 options and one default)
  set_fact: 
      my_var_set_to_be: "{{ 'breakfast' if my_var == 'morning' else 'lunch' if my_var == 'afternoon' else 'dinner' }}"  

- name: Display Variable
  debug: msg="{{ my_var_set_to_be }}"
O.Caliari
  • 313
  • 2
  • 5