Ansible escape string \ - ansible

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

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

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'
...

Ansible sum register value

How to get the sum of two hosts with Jinja2 filtering ansible
host1 and host 2
---
- name: Count Check
hosts: MYGROUP
gather_facts: true
user: sv_admin
tasks:
- name: count check
shell: cat /etc/hosts | wc -l
register: command_result
- debug:
var: command_result.stdout
- set_fact:
total_result: "{{ command_result.stdout | map('int') | sum(start=0) }}"
- debug:
msg: "Total count: {{ total_result }}"
Playbook Output
TASK [debug] *****************************************************************
ok: [Host-01] => {
"msg": "Total count: 134"
}
ok: [Host-02] => {
"msg": "Total count: 133"
}
Use extract and sum. For example, the playbook below
shell> cat playbook.yml
- hosts: test_01:test_03
gather_facts: false
tasks:
- shell: cat /etc/hosts | wc -l
register: command_result
- debug:
var: command_result.stdout
- set_fact:
total_result: "{{ ansible_play_hosts_all|
map('extract', hostvars, ['command_result', 'stdout'])|
map('int')|
sum }}"
run_once: true
- debug:
var: total_result
gives (abridged)
shell> ansible-playbook playbook.yml
PLAY [test_01:test_03] ****
TASK [shell] ****
changed: [test_01]
changed: [test_03]
TASK [debug] ****
ok: [test_01] => {
"command_result.stdout": " 62"
}
ok: [test_03] => {
"command_result.stdout": " 31"
}
TASK [set_fact] ****
ok: [test_01]
TASK [debug] ****
ok: [test_03] => {
"total_result": "93"
}
ok: [test_01] => {
"total_result": "93"
}
See serial
See the difference between ansible_play_hosts and ansible_play_hosts_all
You can use custom stats to do that: https://docs.ansible.com/ansible/latest/modules/set_stats_module.html
So for your case it would look like
---
- name: Count Check
hosts: MYGROUP
gather_facts: true
user: sv_admin
tasks:
- name: count check
shell: cat /etc/hosts | wc -l
register: command_result
- debug:
var: command_result.stdout
- set_fact:
host_result: "{{ command_result.stdout }}"
- debug:
msg: "Count for this host: {{ host_result }}"
- set_stats:
data: "{{ { 'total_count': host_result | int } }}"
Then if you run it with ANSIBLE_SHOW_CUSTOM_STATS=yes it will show you the result at the end:
$ ANSIBLE_SHOW_CUSTOM_STATS=yes ansible-playbook -i inventory pb.yml
... (usual output)
CUSTOM STATS: *************************************************************
RUN: { "total_count": 267}
The set_stats task adds results together from all the hosts by default, which is what you are looking for. You need to make sure the values are integers though, because if they are strings it will just concatenate them and you will end up with something like RUN: { "total_count": "134133"}. That's why I have put the data: bit the way I have - if you try to create the dictionary in regular yaml, like
data:
total_count: "{{ host_result | int }}"
you will see that the value is still a string (due to the way yaml/jinja works) and it won't work properly.

ansible regex_replace IP change

I would like to modify vars for drupal configuration file settings.php with ansible and regex_replace module. My initial var is an IP xxx.xxx.xxx.xxx and I want this as my new var xxx\.xxx\.xxx\.xxx
My playbook:
---
- hosts: localhost
remote_user: cal
become: yes
become_user: cal
tasks:
- set_fact:
ip_front: "10.11.12.13"
ip_front_back: "{{ ip_front | regex_replace('\\.', '\\.') }}"
- name: "show all var"
debug:
msg:
- "{{ ip_front }}"
- "{{ ip_front_ok }}"
Output:
ok: [localhost] => {
"msg": [
"10.11.12.13",
"10\\.11\\.12\\.13"
]
}
How can I use pattern to resolve it?

Variable value for another variable ansible

Sorry if there are many posts about variables inside variable my use case is different.
Trying to access an element from a variable list "efs_list" based on the index-number of the current host. There are three hosts in the inventory
vars:
efs_list:
- efs1
- efs2
- efs3
sdb_index: "{{ groups['all'].index(inventory_hostname) }}"
The values should be as follows
host1- efs1
host2- efs2
host3- efs3
Tried accessing it through efs_list.{{ sdb_index }}
for - debug: var=efs_list.{{ sdb_index }} the output is as intended
ok: [10.251.0.174] => {
"efs_list.0": "efs1"
}
ok: [10.251.0.207] => {
"efs_list.1": "efs2"
}
ok: [10.251.0.151] => {
"efs_list.2": "efs3"
}
But for
- debug:
msg: "{{ efs_list.{{ sdb_index }} }}"
fatal: [10.251.0.174]: FAILED! => {"msg": "template error while templating string: expected name or number. String: {{ efs_list.{{ sdb_index }} }}"}
---
- name: SDB Snapshots Creation
hosts: all
remote_user: "centos"
become: yes
vars:
efs_list:
- efs1
- efs2
- efs3
sdb_index: "{{ groups['all'].index(inventory_hostname) }}"
tasks:
- debug: var=efs_list.{{ sdb_index }}
- debug:
msg: "{{ efs_list.{{ sdb_index }} }}"
- name: Get Filesystem ID
become: false
local_action: command aws efs describe-file-systems --creation-token "{{ efs_list.{{ sdb_index }} }}"
--region us-east-1 --query FileSystems[*].FileSystemId --output text
register: fs_id
It should attribute the element of list to current indexenter code here
extract filter will do the job. The input of the filter must be a list of indices and a container (array in this case). The tasks below
- set_fact:
sdb_index: "{{ [] + [ groups['all'].index(inventory_hostname) ] }}"
- debug:
msg: "{{ sdb_index|map('extract', efs_list)|list }}"
give
ok: [host1] => {
"msg": [
"efs1"
]
}
ok: [host2] => {
"msg": [
"efs2"
]
}
ok: [host3] => {
"msg": [
"efs3"
]
}
If the hosts are not sorted in the inventory it's necessary to sort them in the play
- set_fact:
my_hosts: "{{ groups['all']|sort }}"
- set_fact:
sdb_index: "{{ [] + [ my_hosts.index(inventory_hostname) ] }}"
- debug:
msg: "{{ sdb_index|map('extract', efs_list)|list }}"

Resources