Recently I was writing what initially looked like a pretty straight forward ansible play. Everything was progressing quite well until I came to a point where I needed to set one variable, only when another variable was set. My first pass at this logic was to repeat the task, and control which task was run with a when:
statement. This worked, but just wasn’t ideal with all of the duplicated code. I just knew there had to be a better way and there was.
The Problem
In this situation I needed to set the module parameter parm_b
to the value of a variable var_two
when var_one
was set to a certain value, but in all other cases parameter parm_b
needed to be set to var_three
.
Here’s a cut down version of my first approach:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
---
- name: Handy Ansible Logic
gather_facts: false
hosts: localhost
vars:
one: 1
two: 2
three: 3
tasks:
- name: Set facts for 1
set_fact:
output:
param_a: "{{ one }}"
param_b: "{{ two }}"
when: one == 1
- name: Set facts for not 1
set_fact:
output:
param_a: "{{ one }}"
param_b: "{{ three }}"
when: not one == 1
- name: Print Output
debug:
var: output
Here’s the output of the above play:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
user@box:~/$ ansible-playbook example1.yml
PLAY [Handy Ansible Logic] ********************************
TASK [Set facts for 1] ************************************
ok: [localhost]
TASK [Set facts for not 1] ********************************
skipping: [localhost]
TASK [Print Output] ***************************************
ok: [localhost] => {
"output": {
"param_a": 1,
"param_b": 2
}
}
or, when we pass in a value for var_one
to trigger the other condition:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
user@box:~/$ ansible-playbook example1.yml -e "var_one=2"
PLAY [Handy Ansible Logic] ********************************
TASK [Set facts for 1] ************************************
skipping: [localhost]
TASK [Set facts for not 1] ********************************
ok: [localhost]
TASK [Print Output] ***************************************
ok: [localhost] => {
"output": {
"param_a": 2,
"param_b": 3
}
}
It’s really straight forward and in this simple example the duplication isn’t too bad, but you can see how this would get ugly pretty quickly if you’re using a module that needs lots of parameters to be set, or if you have multiple parameters that need similar conditional logic - that would blow out really quickly. Even just setting 3 parameters with this logic would require 8 different tasks to cover the possibilities and some pretty nasty logic in the when:
statements.
The Solution
The answer to this mess turns out to be quite neat. We move the conditional logic out of the when:
and into the call to the variable, the jinja statement, as variables in ansible are jinja templates. Here is what that would look like:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
---
- name: Handy Ansible Logic
gather_facts: false
hosts: localhost
vars:
var_one: 1
var_two: 2
var_three: 3
tasks:
- name: Set values
set_fact:
output:
param_a: "{{ var_one }}"
param_b: "{{ var_two if var_one == 1 else var_three }}"
- name: Print Output
debug:
var: output
As you can see we have been able to remove the duplicated code and the when statement. What I really like is the if condition doesn’t make the logic hard to read - it’s almost plain english.
Here’s the output of the above improved play logic:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
user@box:~/$ ansible-playbook example2.yml
PLAY [Handy Ansible Logic] ********************************
TASK [Set values] *****************************************
ok: [localhost]
TASK [Print Output] ***************************************
ok: [localhost] => {
"output": {
"param_a": 1,
"param_b": 2
}
}
And, again to trigger the other condition:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
user@box:~/$ ansible-playbook example2.yml -e "var_one=2"
PLAY [Handy Ansible Logic] ********************************
TASK [Set values] *****************************************
ok: [localhost]
TASK [Print Output] ***************************************
ok: [localhost] => {
"output": {
"param_a": 2,
"param_b": 3
}
}
We can still use jinja’s filters like any other variable with this approach too:
1
2
3
4
- name: Set
set_fact:
output:
param_a: "{{ var_two if var_one == 1 else omit }}"
Since learning how to use this logic it has been really helpful in quite a few situations.
All of the examples shown are available here if you would like a copy.