Ansible variable usage. Jinja2 warning about delimiter usage - ansible

I know this looks like a duplicate question about this particular warning, however my use case is using the variable in a way that interprets as a literal string when the {{ brackets }} are removed
ansible 2.8.1
I have the following WORKING Ansible snippet, note the when clause:
- name: Set DNS for data host 1
route53:
command: create
zone: "{{ dns_domain_name }}"
record: "elasticsearch-{{ env }}-01"
type: A
ttl: 300
value: "{{ ec2_private_ip_address }}"
wait: no
private_zone: True
overwrite: yes
when: tag_name is match("xhost-{{ env }}01")
I'm getting the following warning:
[WARNING]: conditional statements should not include jinja2 templating
delimiters such as {{ }} or {% %}. Found: tag_name is
match("xhost-{{ env }}01")
If I remove the delimeter:
when: tag_name is match("xhost-env01")
Won't work since it's part of the match() query string. I get no errors but the match query doesn't work.
How can I handle this without getting the annoying warnings?
Thanks!

You can create an additional variable and then use the variable in match().
- name: Set DNS for data host 1
route53:
command: create
zone: "{{ dns_domain_name }}"
record: "elasticsearch-{{ env }}-01"
type: A
ttl: 300
value: "{{ ec2_private_ip_address }}"
wait: no
private_zone: True
overwrite: yes
when: tag_name is match(matcher)
vars:
matcher: "xhost-{{ env }}01"

Related

Ansible condtional statement with stdout and variable

I am trying to use a condition to verify the MD5_hash value from the std output with the stored value in the host file.
- block:
- name: Install OS
nxos_install_os:
provider: "{{ creds }}"
system_image_file: "{{bin}}"
issu: no
timeout: 500
when: md5_result.stdout[0] == "{{ MD5_Hash }}"
[WARNING]: conditional statements should not include jinja2 templating delimiters such as {{ }} or {% %}. Found:
md5_result.stdout[0] == "{{ MD5_bin5 }}"
I found a suggestion of using [] instead of {{}}, but I am just getting the condition result is FALSE.
Please suggest.

Ansible set fact: evaluate a json path with a variable

I need to fetch data with conditions. This piece of code worked for me.
- name: retriving passwords...
uri:
...........
register: this
- name: Setting ssh secret fact...
set_fact:
ssh_secret: "{{ this.json.data.data.devuservalue }}"
when: ansible_user == "{{ user_dev }}"
The value of {{ user_dev }} is also devuservalue.
How can I remove this devuservalue hardcoded value from ssh_secret: line. Because I'll have to add several set facts with conditions. If I could add variable within variable.

Select with_items with conditional on each item in Ansible task?

I have a list of proxies that need to be selected based on the cluster location. For example, my cluster names are 'abc' and 'def'. All nodes on clusters start with the cluster name (e.g. abc1023.net for abc etc.)
I want to select the proxies for pip based on the current inventory_hostname and provide it in the arguments. I tried to use the map of with_items and 'creating' the when condition within the map as per the code below:
- name: run pip on proxy
pip:
name: <package_name>
extra_args: "--proxy item.proxy"
when: "item.when"
with_items:
- {proxy: 'http:abc_proxy:port', when: "'abc' in {{inventory_hostname|lower}}"}
- {proxy: 'http:def_proxy:port', when: "'def' in {{inventory_hostname|lower}}"}
The problem I am facing is that this condition is always perceived as true. I tried replacing when: "'abc' in {{inventory_hostname|lower}}" to when: false and that actually works. That is, making it an explicit false actually returns false but not when I check the condition in string quotes. I think this when within the map is just perceived as true if it contains any value.
How do I explicitly check this condition in the when map? Removing the quotes does not help as it throws syntactical error:
We could be wrong, but this one looks like it might be an issue
with missing quotes. Always quote template expression brackets when
they start a value. For instance:
with_items:
- {{ foo }}
Should be written as:
with_items:
- "{{ foo }}"
exception type: <class 'yaml.parser.ParserError'>
Other solutions tried
Added the vars within the task
- name: run pip on proxy
vars:
abc_status: "{{ true if 'abc' in {{inventory_hostname|lower}} else false }}"
def_status: "{{ true if 'def' in {{inventory_hostname|lower}} else false }}"
pip:
name: <package_name>
extra_args: "--proxy item.proxy"
when: "item.when"
with_items:
- {proxy: 'http:abc_proxy:port', when: abc_status}
- {proxy: 'http:def_proxy:port', when: def_status}
2.Added the task to set_fact
- set_fact:
abc_status: true
when: inventory_hostname|lower is match('abc.*')
- set_fact:
def_status: true
when: inventory_hostname|lower is match('def.*')
Tested the false case
I tested the false case in the following ways on abc cluster:
- name: run pip on proxy
vars:
abc_status: "{{ true if 'abc' in {{inventory_hostname|lower}} else false }}"
def_status: "{{ true if 'def' in {{inventory_hostname|lower}} else false }}"
pip:
name: <package_name>
extra_args: "--proxy item.proxy"
when: "item.when"
with_items:
- {proxy: 'http:def_proxy:port', when: def_status}
This should always fail as the proxy as well as the when condition is checking on def cluster whereas it is running on abc cluster. But I get the following Ansible output:
TASK [<project> : run pip on proxy] ************************************************
changed: [abc1023.net] => (item={u'when': u'def_status', u'proxy': u'http:def_proxy:port'})
This is the output I always get with other tried solutions as well.
Question
Even after trying above different solutions, when: "item.when" always return true (even when it should return false). How can I fix this? Is there any better solution to implement my use case?
For completeness, I am using ansible 2.4.1.0.
TL;DR;
Here, you are trying to assess that the string "item.when" is a boolean that is true, which it is, because, a non empty string will result in a true statement.
Remove your doubles quotes around the when condition and you should be good to go.
The warning you get from Ansible is about when and only this statement, which is always a raw Jinja2 expression.
This is easy to do in Ansible with the when clause, which contains a raw Jinja2 expression without double curly braces (see group_by – Create Ansible groups based on facts).
Source: https://docs.ansible.com/ansible/latest/user_guide/playbooks_conditionals.html#the-when-statement, emphasis, mine.
Given your actual playbook, a possible solution could be:
- name: run pip on proxy
pip:
name: <package_name>
extra_args: "--proxy {{ item.proxy }}"
when: item.proxy_ref in inventory_hostname|lower
with_items:
- proxy: 'http:abc_proxy:port'
proxy_ref: 'abc'
- proxy: 'http:def_proxy:port'
proxy_ref: 'def'
Another one could be:
- name: run pip on proxy
pip:
name: <package_name>
extra_args: "--proxy {{ item.proxy }}"
when: item.when
with_items:
- proxy: 'http:abc_proxy:port'
when: "{{ 'abc' in inventory_hostname|lower }}"
- proxy: 'http:def_proxy:port'
when: "{{ 'def' in inventory_hostname|lower }}"
So, in short, here, you are trying to assess that the string "item.when" is a boolean that is true, which it is, because, a non empty string will result in a true statement.
Remove your doubles quotes around the when condition and you should be good to go.
PS: try not to mix the JSON syntax with the YAML one, when possible
Q: "Is there any better solution to implement my use case?"
A: You might be better off with the concatenation of the proxy. For example
- name: run pip on proxy
pip:
name: "{{ package_name }}"
extra_args: "--proxy {{ my_proxy }}"
vars:
my_prefix: "{{ inventory_hostname[0:3] }}"
my_proxy: "{{ 'http:' ~ my_prefix ~ '_proxy:port' }}"

How to use ansible when condition when string contains '#'

I am writing playbook to check user principal in kerberos servers. If principal exists it should skip task and if not it should create user principal. I am not sure how to use string with when condition I am trying below but getting errors
"ERROR! Syntax Error while loading YAML.
expected <block end>, but found '<scalar>'
The error appears to be in '/home/revyas/RHELProjects/Atlas/eda-datalake/playbooks/provision-emr.yml': line 42, column 31, but may
be elsewhere in the file depending on the exact syntax problem.
The offending line appears to be:
stat:
path: "{{ user_dir }}"/"{{ keytab_name }}"
^ here
We could be wrong, but this one looks like it might be an issue with
missing quotes. Always quote template expression brackets when they
start a value. For instance:
with_items:
- {{ foo }}
Should be written as:
with_items:
- "{{ foo }}"
Playbook:
- name: Check if user principals exist
command: 'kadmin -w "{{ emr_kdc_admin_password }}" -p kadmin/admin listprincs'
register: user_princs
delegate_to : "{{ emr_kerberos_master }}"
tags: "emr_acct"
- name: Create user kerberos principal if not exist
command: 'kadmin -w {{ emr_kdc_admin_password }} -p kadmin/admin addprinc -randkey {{ kerberos_username }}#{{ emr_kerberos_realm }}'
when: "{{ kerberos_username }}#{{ emr_kerberos_realm }}" not in user_princs.stdout
delegate_to: "{{ emr_kerberos_master }}"
tags: "emr_acct"
User principal from kdc have format given below:
emr-test1-aren-reetika#abd.xyz.com
emr-test-aren#bd.xyz.com
emr-test-integration-test#bd.xyz.com
For the first cited issue, yaml doesn't behave like python or shell which automatically concatenate string literals together
You'll want:
stat:
path: "{{ user_dir }}/{{ keytab_name }}"
And the second error is because yaml believes the leading " is the start of a YAML literal, but in fact it's the start of a Jinja2 literal, thus:
when: '"{{ kerberos_username }}#{{ emr_kerberos_realm }}" not in user_princs.stdout'
Or you can use any of the scalar folding syntaxes, if you prefer that:
when: >-
"{{ kerberos_username }}#{{ emr_kerberos_realm }}"
not in user_princs.stdout
when: "{{ kerberos_username }}#{{ emr_kerberos_realm }}" not in user_princs.stdout
change to
when: "{{ kerberos_username }}\\#{{ emr_kerberos_realm }}" not in user_princs.stdout

Conditionally define variable in Ansible

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?
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.
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.
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.
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"
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 }}"

Resources