20

Is it possible to change a role variable default value according to some condition (i.e. the value of another variable)?

Details

I have two related variables for a command, env and composer_opts.

If both are left at default (env = "prod" and composer_opts = "--no-dev") everything is ok.

If I change env to dev, the default for the other one will break my command, so I always need to set both. Would it be possible to avoid this by setting a conditional default value with a custom script / if?

Important: I don't want to always set the composer_opts value according to the env value. I want to set it only if it's not already set (i.e. a dynamic default value).

Pseudocode

I would like to do something like this (following code is not valid, just pseudocode to express my need)

---
# defaults/main.yml

env: prod
composer_opts: 
    when: "{{env}}" = 'prod'
        '--no-dev --optimize-autoloader --no-interaction'
    when: "{{env}}" = 'dev'
        '' 

4 Answers 4

14

I suggest this solution:

---
 - set_fact:
     composer_opts: ""
   when: "{{env}}" == 'dev'

It will set composer_opts variable to string "" when variable env is equal to 'dev'.

Here is example of playbook based on updated question:

$ cat test.yml

---
- hosts: 127.0.0.1
  connection: local
  tasks:
  - set_fact:
      composer_opts: "{% if env == 'prod' %} '--no-dev --optimize-autoloader --no-interaction' {% else %} '' {% endif %}"

  - debug: var=composer_opts

Sample output:

sudo ansible-playbook test.yml -e env=dev

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

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

TASK: [set_fact ] ************************************************************* 
ok: [127.0.0.1]

TASK: [debug var="{{composer_opts}}"] ***************************************** 
ok: [127.0.0.1] => {
    "var": {
        " '' ": " '' "
    }
}

PLAY RECAP ******************************************************************** 
127.0.0.1                  : ok=3    changed=0    unreachable=0    failed=0   


sudo ansible-playbook test.yml -e env=prod

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

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

TASK: [set_fact ] ************************************************************* 
ok: [127.0.0.1]

TASK: [debug var="{{composer_opts}}"] ***************************************** 
ok: [127.0.0.1] => {
    "var": {
        " '--no-dev --optimize-autoloader --no-interaction' ": " '--no-dev --optimize-autoloader --no-interaction' "
    }
}

PLAY RECAP ******************************************************************** 
127.0.0.1                  : ok=3    changed=0    unreachable=0    failed=0   
7
  • 1
    This is a part of the solution. It will always set composer_opts to empty string when env is "dev", overwriting any actual value set. I think the conditional should be extended like this: when: "{{env}}" == 'dev' and "{{composer_opts}}" is undefined. Does it looks good? Can you udpate your question accordingly? Aug 21, 2015 at 11:05
  • composer_opts will be defined because it have it's default value. You need another expression to solve your task. For example variable custom_composer_opts.
    – Navern
    Aug 21, 2015 at 12:00
  • Please elaborate with pseudocode what do you want to do. I will update my answer accordingly.
    – Navern
    Aug 21, 2015 at 12:01
  • I have updated my question with additional explanation and pseudocode sample. Thank you. Aug 21, 2015 at 12:14
  • I have updated my answer. Check it. I believe i understood what you need.
    – Navern
    Aug 21, 2015 at 12:37
7

While @Navern's answer does work, I found the embedded Jinja2 notation ("{% if env == 'prod' %} ...) to be extremely susceptible to notation and thus rather fragile. For example, when wrapping the line in question for better readability such as in this untested code:

composer_opts: >
               "{% if env == 'prod' %}
                   '--no-dev --optimize-autoloader --no-interaction'
                {% else %}
                   ''
                {% endif %}"

I ended up with unexpected results, such as additional whitespace or \n in composer_opts.

The approach I use is much dumber, but also more stable:

- name: set composer_opts for dev env
  set_fact:
     composer_opts: ''
     when: "{{env}}" == 'dev'

- name: set composer_opts for prod env
  set_fact:
     composer_opts: '--no-dev --optimize-autoloader --no-interaction'
     when: "{{env}}" == 'prod'

I also found this blog post to be useful which essentially follows the same approach.

3
  • @sec if you use | instead of > you might not have the whitespace problem. (or you'll get more of it LOL)
    – Michael
    May 17, 2017 at 8:40
  • @sec Use '>-' and check out the ansible spec. It has many options for correctly manipulating multiline strings. yaml-multiline.info Note, in particular, the block chomping indicator.
    – DylanYoung
    Oct 10, 2019 at 13:23
  • Note that this solution also had precedence issues. A fact is not a default.
    – DylanYoung
    Oct 10, 2019 at 13:24
3

Ansible set_fact based on condition in one liner :

- name: "set composer_opts based on environment"
  set_fact:
     composer_opts:  "{{ '--no-dev --optimize-autoloader --no-interaction' if (env == 'prod') else '' }}"
3
  • Same precedence issues as the other solutions ( a fact is not a default), however, if you put this condiional right in your defaults.yml file, this solution will work. It gets real ugly real fast if you have a number of defaults dependent on the condition
    – DylanYoung
    Oct 10, 2019 at 13:25
  • IMO, we can have the variable in global vars file so that it could be imported wherever it is required. Apr 8, 2020 at 15:41
  • 1
    Imported how? That doesn't change the precedence. In particular, this fact will override host vars, unlike a default. You could add a condition though: when: composer_opts is not defined or something to that effect.
    – DylanYoung
    Apr 8, 2020 at 20:28
0
- name: 

set_fact: path_install: | {% if reportviewer_state == 'absent' and reportviewer_handler[reportviewer_package].name == 'ReportViewer_2010.exe' %} C:\Windows\System32\msiexec.exe {% else %} {{ url_base_repository }}{{ reportviewer_handler[reportviewer_package].name }} {% endif %}

  • name: "install {{ reportviewer_handler[reportviewer_package].name }}" win_package: path: "{{ path_install | replace('\n', '') }}" state: "{{ reportviewer_state }}" product_id: "{{ reportviewer_handler[reportviewer_package].product_id }}" arguments: "{{ reportviewer_handler[reportviewer_package].arguments }}"
1
  • I think it would be good to add some more context around this answer
    – Daniel K
    May 18, 2022 at 11:55

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.