How to format strings with jinja/ansible? - ansible

Friends,
I am learning Ansible here and trying to apply what I am learning to one of my simple projects. I got stuck trying to format some strings. I getting the following error:
fatal: [localhost]: FAILED! => {"changed": false, "msg":
"AnsibleError: template error while templating string:
**unexpected ']', expected ')'**.
String:
cluster_name = \"{{ terraform['%s' | format(env)].cluster_name }}\"
\ncredentials = \"{{ terraform['%s' | format(env)].credentials }}\"
\ninitial_node_count = \"{{ terraform['%s' | format(env]].initial_node_count }}
\format(env)].project }}\""}
...
This is how my jinja2 file looks like:
cluster_name = "{{ terraform['%s' | format(env)].cluster_name }}"
credentials = "{{ terraform['%s' | format(env)].credentials }}"
initial_node_count = "{{ terraform['%s' | format(env]].initial_node_count }}"
...
I want to generate strings like this terraform['testing'].credentials when I run ansible-playbook site.yaml -e env=testingand write them to a file. I can't figure out another way to get this done. Any Idea?

you want to use this syntax:
cluster_name = "{{ terraform[env].cluster_name }}"
With terraform.env, ansible will get the env key of the terraform env, but with terraform[env]
, ansible will resolve env to 'testing' and get the 'testing' key of the terraform var.

Q: "Generate strings like this terraform['testing'].credentials"
shell> ansible-playbook site.yaml -e env=testing
A: The playbook below
shell> cat site.yml
- hosts: localhost
tasks:
- set_fact:
cluster_name: "{{ begin }}{{ env }}{{ end }}"
vars:
begin: "terraform['"
end: "'].credentials"
- debug:
var: cluster_name
gives
cluster_name: terraform['testing'].credentials

Related

Ansible lint : Found a bare variable

This is my ansible script.
- name: Validate that blacklisted URLs are unreachable
environment:
SSL_CERT_FILE: "{{ ssl_cert_path }}"
ansible.builtin.uri:
url: "{{ item }}"
timeout: 10
register: blacklisted_http_responses
with_lines: cat {{ role_path }}/files/blacklisted_urls.txt
And i am getting this lint error for the sbove code
Found a bare variable 'cat {{ role_path }}/files/blacklisted_urls.txt' used in a 'with_lines' loop.
any idea how to resolve this ? I tried Putting the variable name in double quotes.
What you see is very probably an ansible-lint issue. You should use loop instead of with_lines. There are no complaints by ansible-lint about the code below
loop: "{{ lookup('file',
role_path ~ '/files/blacklisted_urls.txt').splitlines() }}"
You can also use the pipe lookup plugin instead of the file if you want to. The loop below gives the same result
loop: "{{ lookup('pipe',
'cat ' ~ role_path ~ '/files/blacklisted_urls.txt').splitlines() }}"
For example, the playbook
shell> cat pb.yml
---
- hosts: localhost
roles:
- role_a
the role
shell> cat roles/role_a/tasks/main.yml
---
- name: Debug
debug:
var: item
loop: "{{ lookup('file',
role_path ~ '/files/blacklisted_urls.txt').splitlines() }}"
and the file
shell> cat roles/role_a/files/blacklisted_urls.txt
www1.example.com
www2.example.com
give (abridged)
TASK [role_a : Debug] ****************************************************
ok: [localhost] => (item=www1.example.com) =>
ansible_loop_var: item
item: www1.example.com
ok: [localhost] => (item=www2.example.com) =>
ansible_loop_var: item
item: www2.example.com

Ansible nested braces or nested quotes

Using ansible 2.9 I set a variable like this to store part of a group name
- name: set group
set_fact:
ansible_group: aaaa
I then want to use this variable in the following with_items clause:
- name: get
uri:
url: "http://{{ item }}:5324/kjhfg"
with_items: "{{ groups['thisgroup_{{ ansible_group }}'] }}"
However, using nested curly braces gives me the following error:
FAILED! => {"msg": "'dict object' has no attribute 'thisgroup_{{ ansible_group }}'"}
I also tried the following syntax variations
with_items: "{{ groups['thisgroup_ansible_group'] }}"
with_items: "groups['thisgroup_{{ ansible_group }}']"
with_items: "{{ groups['thisgroup_hostvars['localhost']['ansible_group']'] }}"
with_items: "{{ groups['thisgroup_hostvars[''localhost''][''ansible_group'']'] }}"
with_items: "{{ groups['thisgroup_hostvars[`localhost`][`ansible_group`]'] }}"
and probably one hundred other variations which all of them produced various errors
Can someone help me figure out the right syntax?
Simply concatenate the name of the group, e.g. given the inventory
shell> cat hosts
[thisgroup_aaaa]
host1
host2
host3
the playbook
- hosts: all
gather_facts: false
vars:
ansible_group: aaaa
_group: "{{ groups['thisgroup_' ~ ansible_group] }}"
tasks:
- debug:
var: item
loop: "{{ _group }}"
run_once: true
gives
item: host1
item: host2
item: host3

Is it possible to set variable in variable with ansible?

For example, set a username for each different service as
- name: Add the user 'user_xxx' with password
ansible.builtin.user:
name: "{{ user_name_{{svc_name}} }}"
password: "{{ user_password_{{svc_name}} | password_hash('sha512') }}"
In order to set dynamic values to name and password, use thess variables in group_vars:
user_name_a
user_name_b
user_name_c
user_password_a
user_password_b
user_password_c
But want to read a, b, c from an environment variable as:
export SERVICE_NAME=a
playbook:
vars:
svc_name: "{{ lookup('env', 'SERVICE_NAME') }}"
Then compile 2 kinds of variables together to make a final value.
When I tried to do it, I got
fatal: [server]: FAILED! => {"msg": "template error while templating string: expected token 'end of print statement', got '{'. String: {{ user_name_{{svc_name}} }}"}
For example, given the group_vars
shell> cat group_vars/all.yml
user_name_a: userA
user_name_b: userB
user_name_c: userC
user_password_a: psswdA
user_password_b: psswdB
user_password_c: psswdC
the playbook
shell> cat playbook.yml
- hosts: localhost
vars:
svc_name: "{{ lookup('env', 'SERVICE_NAME') }}"
tasks:
- debug:
msg: "name: {{ lookup('vars', 'user_name_' ~ svc_name) }},
pswd: {{ lookup('vars', 'user_password_' ~ svc_name) }}"
gives
shell> SERVICE_NAME=b ansible-playbook playbook.yml
PLAY [localhost] ****************************************************************
TASK [debug] ********************************************************************
ok: [localhost] =>
msg: 'name: userB, pswd: psswdB'
...

Regex match for key in jinja2 selectattr()

Given the data:
"serverName", [
serverData: [
{
"internal_ip": "10.1.1.100",
"external_ip": "172.16.1.10",
"name": "dns-1"
},
],
]
This extracts the name value dns-1 when the internal_ip matches the equalto. So far, so good.
- debug:
msg: "{{ mydict | selectattr('internal_ip', 'equalto', '10.1.1.100') |
map(attribute='name') | list }}"
In the real problem, I do not know which type of *_ip will the ip address I'm searching for will reside. It could be under internal_ip, it could be under external_ip and for all I know, there could be even more options, the only thing that will always be there - is the actual IP address I'm searching for: '10.1.1.100`.
So I need to regex match like so:
- debug:
msg: "{{ mydict | selectattr('^.*$', 'equalto', '10.1.1.100') |
map(attribute='name') | list }}"
I'm not sure if this is possible, but it seems to be one of the ways out of this jam.
For example, the playbook
shell> cat playbook.yml
- hosts: localhost
vars:
mylist:
- {internal_ip: 10.1.1.101, external_ip: 172.16.1.10, name: dns-1}
- {internal_ip: 10.1.1.102, external_ip: 172.16.1.10, name: dns-2}
- {internal_ip: 10.1.1.103, external_ip: 172.16.1.10, name: dns-3}
tasks:
- set_fact:
sel: "{{ sel|default([]) + [item.name] }}"
loop: "{{ mylist }}"
when: sel_ip|default('') in item.values()|list
- debug:
var: sel
gives
shell> ansible-playbook playbook.yml -e sel_ip=172.16.1.10
...
sel:
- dns-1
- dns-2
- dns-3
shell> ansible-playbook playbook.yml -e sel_ip=10.1.1.103
...
sel:
- dns-3

Ansible escape string \

Trying to add \ before . on a list of IPs in Ansible.
Example:
"msg": "192.168.5.0"
Expected output:
"msg": "192\.168\.5\.0"
I tried this with no luck.
---
- hosts: localhost
vars:
ip: "{{ ansible_default_ipv4.network }}"
tasks:
- debug: msg="{{ ip | regex_replace('\.', '\\.')}}"
Output:
ok: [localhost] => {
"msg": "192\\.168\\.5\\.0"
}
---
- hosts: localhost
vars:
ip: "{{ ansible_default_ipv4.network }}"
tasks:
- debug: msg="{{ ip | regex_replace('.', '\.')}}"
Output:
ok: [localhost] => {
"msg": "192\\.168\\.5\\.0"
}
The function regex_replace works as you expect. The double backslashes you see are the evaluation of the string by debug. Display, for example, the length of the string
- set_fact:
ip2: "{{ ip | regex_replace(myregex, myreplace) }}"
vars:
myregex: '\.'
myreplace: '\.'
- debug:
msg: "length of {{ ip2 }} is {{ ip2|length }}"
give
"msg": "length of 192\\.168\\.5\\.0 is 14"
It's also possible to write the string to a file. For example the template
shell> cat test.txt.j2
{{ ip2 }}
and the task
- template:
src: test.txt.j2
dest: test.txt
give
shell> cat test.txt
192\.168\.5\.0

Resources