how to create conditional list in ansible task
If possible I would like to combine the two tasks below into a single task. Rather than use the when
conditional, is there some way to add the last two list items under subject_alt_name
(second task) only when item.0.sans
is defined? These two tasks are otherwise identical.
I did try some additional filters for those lines to default/omit them, but then I ended up with errors that the list could not be parsed. I also explored the possibility of an inline template, but I cannot tell if that is supported in this context. A templating solution seems ideal to me... I think that technique could be useful in many other scenarios.
The full playbook is pretty short, so I can post that if it would be helpful for additional context.
- name: generate endpoint CSR - no custom sans
when: item.0.sans is undefined
community.crypto.openssl_csr:
common_name: "{{item.0.name}}.{{item.1}}"
path: "{{endpoint_artifact_dir}}/{{item.1}}//CSRs/{{item.0.name}}.{{item.1}}.pem"
privatekey_path: "{{endpoint_artifact_dir}}/{{item.1}}/keys/{{item.0.name}}.{{item.1}}.pem"
subject_alt_name:
- "DNS:{{item.0.name}}.{{item.1}}"
- "DNS:{{item.0.name}}"
- "IP:127.0.0.1"
with_nested:
- "{{endpoints}}"
- "{{domains}}"
- name: generate endpoint CSR - with custom sans
when: item.0.sans is defined
community.crypto.openssl_csr:
common_name: "{{item.0.name}}.{{item.1}}"
path: "{{endpoint_artifact_dir}}/{{item.1}}/CSRs/{{item.0.name}}.{{item.1}}.pem"
privatekey_path: "{{endpoint_artifact_dir}}/{{item.1}}/keys/{{item.0.name}}.{{item.1}}.pem"
subject_alt_name:
- "DNS:{{item.0.name}}.{{item.1}}"
- "DNS:{{item.0.name}}"
- "IP:127.0.0.1"
- "{{ item.0.sans | join(',') | replace(',', '.' + item.1 + ', ') + '.' + item.1 }}"
- "{{ item.0.sans | join(',') }}"
with_nested:
- "{{endpoints}}"
- "{{domains}}"
Solution 1:
Use the filter ternary. For example, given the data
endpoints1:
- {name: srv1, sans: [alt1, alt2]}
- {name: srv2, sans: [alt1, alt2]}
endpoints2:
- {name: srv1}
- {name: srv2}
domains:
- exampleA.com
- exampleB.com
The task below
- debug:
msg: |-
subject_alt_name:
{{ _san|to_nice_yaml|indent(2) }}
with_nested:
- "{{ endpoints1 }}"
- "{{ domains }}"
vars:
_san1:
- "DNS:{{ item.0.name }}.{{ item.1 }}"
- "DNS:{{ item.0.name }}"
- "IP:127.0.0.1"
_sans: "{{ item.0.sans|default([]) }}"
_san2:
- "{{ _sans|join(',')|replace(',', '.' + item.1 + ', ') + '.' + item.1 }}"
- "{{ _sans|join(', ') }}"
_san: "{{ (_sans|length > 0)|ternary( _san1 + _san2, _san1) }}"
gives
msg: |-
subject_alt_name:
- DNS:srv1.exampleA.com
- DNS:srv1
- IP:127.0.0.1
- alt1.exampleA.com, alt2.exampleA.com
- alt1, alt2
msg: |-
subject_alt_name:
- DNS:srv1.exampleB.com
- DNS:srv1
- IP:127.0.0.1
- alt1.exampleB.com, alt2.exampleB.com
- alt1, alt2
msg: |-
subject_alt_name:
- DNS:srv2.exampleA.com
- DNS:srv2
- IP:127.0.0.1
- alt1.exampleA.com, alt2.exampleA.com
- alt1, alt2
msg: |-
subject_alt_name:
- DNS:srv2.exampleB.com
- DNS:srv2
- IP:127.0.0.1
- alt1.exampleB.com, alt2.exampleB.com
- alt1, alt2
The same task with the list endpoints2
with_nested:
- "{{ endpoints2 }}"
- "{{ domains }}"
will omit the attribute sans
msg: |-
subject_alt_name:
- DNS:srv1.exampleA.com
- DNS:srv1
- IP:127.0.0.1
msg: |-
subject_alt_name:
- DNS:srv1.exampleB.com
- DNS:srv1
- IP:127.0.0.1
msg: |-
subject_alt_name:
- DNS:srv2.exampleA.com
- DNS:srv2
- IP:127.0.0.1
msg: |-
subject_alt_name:
- DNS:srv2.exampleB.com
- DNS:srv2
- IP:127.0.0.1
Notes
- Optionally, use product and map to create the hosts, e.g.
_san2:
- "{{ _sans|product([item.1])|map('join', '.')|join(', ') }}"
- "{{ _sans|join(', ') }}"
- mre would be
endpoints:
- {name: srv1, sans: alt1}
- {name: srv2}
- debug:
msg: |-
subject_alt_name:
{{ _san|to_nice_yaml|indent(2) }}
loop: "{{ endpoints }}"
vars:
_san1:
- "A: {{ item.name }}"
- "B: {{ item.name }}"
_san2:
- "C: {{ item.sans|default('') }}"
_san: "{{ (item.sans|default('')|length > 0)|
ternary( _san1 + _san2, _san1) }}"
gives
msg: |-
subject_alt_name:
- 'A: srv1'
- 'B: srv1'
- 'C: alt1'
msg: |-
subject_alt_name:
- 'A: srv2'
- 'B: srv2'