Ansible lint : Found a bare variable - ansible

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

Related

Ansible Executing Nested Variable Loops on Multiple Files

I want to perform the following command using an Ansible playbook:
sed -i.bak 's/^mirror/#mirror/;s/#baseurl/baseurl/;s/mirror.centos/vault.centos/' /etc/yum.repos.d/CentOS-*.repo
A basic working Ansible playbook looks like this:
---
- name: Resolve any CentOS issues
hosts: "{{ nodes }}"
tasks:
- shell: sed -i.bak 's/^mirror/#mirror/;s/#baseurl/baseurl/;s/mirror.centos/vault.centos/' /opt/yum.repos.d/CentOS-*.repo
However, I would like to create a more advanced playbook, which *might *look something like this:
---
- name: Modify CentOS Reps
hosts: localhost
vars:
filelist: []
reg:
- ^mirrorlist
- ^#baseurl
- mirror.centos
rep:
- #mirror
- baseurl
- vault.centos
tasks:
- name: Find matching repo files and register to a variable
find:
paths: /etc/yum.repos.d
file_type: file
recurse: no
patterns: CentOS-*.repo
register: output
- name: Adding filelist to the LIST
no_log: true
set_fact:
filelist: "{{ filelist + [item.path]}}"
with_items: "{{ output.files }}"
- debug: var=filelist
- name: Change the lines
replace:
path: "{{ item.0 }}"
regexp: "{{ item.1.regexp }}"
replace: "{{ item.1.replace }}"
backup: true
with_nested:
- "{{ filelist }}"
- '{ regexp: "{{ reg }}", replace: "{{ rep }}" }'
The above gives the following attribute error:
fatal: [localhost]: FAILED! => {"msg": "The task includes an option with an undefined variable. The error was: 'ansible.utils.unsafe_proxy.AnsibleUnsafeText object' has no attribute 'regexp'\n\nThe error appears to be in '/etc/ansible/playbooks/centos-correct-repo301.yml': line 33, column 6, but may\nbe elsewhere in the file depending on the exact syntax problem.\n\nThe offending line appears to be:\n\n\n - name: Change the lines\n ^ here\n"}
Any thoughts?
Thanks
There are more options. For example,
Put the regex/replace logic into a dictionary
reg_rep:
- {reg: '^mirror', rep: '#mirror'}
- {reg: '^#baseurl', rep: 'baseurl'}
- {reg: 'mirror.centos', rep: 'vault.centos'}
Declare the list of the files
filelist: "{{ output.files|map(attribute='path')|list }}"
and find the files (no changes to your code in this task)
- name: Find matching repo files and register to a variable
find:
paths: /etc/yum.repos.d
file_type: file
recurse: no
patterns: CentOS-*.repo
register: output
Update the configuration
- name: Change the lines
replace:
path: "{{ item.0 }}"
regexp: "{{ item.1.reg }}"
replace: "{{ item.1.rep }}"
backup: true
with_nested:
- "{{ filelist }}"
- "{{ reg_rep }}"
loop_control:
label: "{{ item.0|basename }}"
Example of a complete playbook for testing
shell> cat pb.yml
- hosts: test_24
vars:
reg_rep:
- {reg: '^mirror', rep: '#mirror'}
- {reg: '^#baseurl', rep: 'baseurl'}
- {reg: 'mirror.centos', rep: 'vault.centos'}
filelist: "{{ output.files|map(attribute='path')|list }}"
tasks:
- name: Find matching repo files and register to a variable
find:
paths: /etc/yum.repos.d
file_type: file
recurse: no
patterns: CentOS-*.repo
register: output
- debug:
var: filelist
- debug:
msg: |
path: "{{ item.0 }}"
regexp: "{{ item.1.reg }}"
replace: "{{ item.1.rep }}"
backup: true
with_nested:
- "{{ filelist }}"
- "{{ reg_rep }}"
loop_control:
label: "{{ item.0|basename }}"
when: debug|d(false)|bool
- name: Change the lines
replace:
path: "{{ item.0 }}"
regexp: "{{ item.1.reg }}"
replace: "{{ item.1.rep }}"
backup: true
with_nested:
- "{{ filelist }}"
- "{{ reg_rep }}"
loop_control:
label: "{{ item.0|basename }}"
gives abridged, running in --check --diff mode
shell> ansible-playbook pb.yml -CD
PLAY [test_24] *******************************************************************************
TASK [Find matching repo files and register to a variable] ***********************************
ok: [test_24]
TASK [debug] *********************************************************************************
ok: [test_24] =>
filelist:
- /etc/yum.repos.d/CentOS-Linux-ContinuousRelease.repo
- /etc/yum.repos.d/CentOS-Linux-Debuginfo.repo
- /etc/yum.repos.d/CentOS-Linux-Devel.repo
- /etc/yum.repos.d/CentOS-Linux-Extras.repo
- /etc/yum.repos.d/CentOS-Linux-FastTrack.repo
- /etc/yum.repos.d/CentOS-Linux-HighAvailability.repo
- /etc/yum.repos.d/CentOS-Linux-Media.repo
- /etc/yum.repos.d/CentOS-Linux-Plus.repo
- /etc/yum.repos.d/CentOS-Linux-PowerTools.repo
- /etc/yum.repos.d/CentOS-Linux-Sources.repo
- /etc/yum.repos.d/CentOS-Linux-BaseOS.repo
- /etc/yum.repos.d/CentOS-Linux-AppStream.repo
TASK [debug] *********************************************************************************
skipping: [test_24] => (item=CentOS-Linux-ContinuousRelease.repo)
skipping: [test_24] => (item=CentOS-Linux-ContinuousRelease.repo)
...
skipping: [test_24] => (item=CentOS-Linux-AppStream.repo)
skipping: [test_24]
TASK [Change the lines] **********************************************************************
--- before: /etc/yum.repos.d/CentOS-Linux-ContinuousRelease.repo
+++ after: /etc/yum.repos.d/CentOS-Linux-ContinuousRelease.repo
## -17,7 +17,7 ##
[cr]
name=CentOS Linux $releasever - ContinuousRelease
-mirrorlist=http://mirrorlist.centos.org/?release=$releasever&arch=$basearch&repo=cr&infra=$infra
+#mirrorlist=http://mirrorlist.centos.org/?release=$releasever&arch=$basearch&repo=cr&infra=$infra
#baseurl=http://mirror.centos.org/$contentdir/$releasever/cr/$basearch/os/
gpgcheck=1
enabled=0
changed: [test_24] => (item=CentOS-Linux-ContinuousRelease.repo)
--- before: /etc/yum.repos.d/CentOS-Linux-ContinuousRelease.repo
+++ after: /etc/yum.repos.d/CentOS-Linux-ContinuousRelease.repo
## -18,7 +18,7 ##
[cr]
name=CentOS Linux $releasever - ContinuousRelease
mirrorlist=http://mirrorlist.centos.org/?release=$releasever&arch=$basearch&repo=cr&infra=$infra
-#baseurl=http://mirror.centos.org/$contentdir/$releasever/cr/$basearch/os/
+baseurl=http://mirror.centos.org/$contentdir/$releasever/cr/$basearch/os/
gpgcheck=1
enabled=0
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
changed: [test_24] => (item=CentOS-Linux-ContinuousRelease.repo)
...
--- before: /etc/yum.repos.d/CentOS-Linux-AppStream.repo
+++ after: /etc/yum.repos.d/CentOS-Linux-AppStream.repo
## -11,7 +11,7 ##
[appstream]
name=CentOS Linux $releasever - AppStream
mirrorlist=http://mirrorlist.centos.org/?release=$releasever&arch=$basearch&repo=AppStream&infra=$infra
-#baseurl=http://mirror.centos.org/$contentdir/$releasever/AppStream/$basearch/os/
+#baseurl=http://vault.centos.org/$contentdir/$releasever/AppStream/$basearch/os/
baseurl=http://vault.centos.org/$contentdir/$releasever/AppStream/$basearch/os/
gpgcheck=1
enabled=0
changed: [test_24] => (item=CentOS-Linux-AppStream.repo)
PLAY RECAP ***********************************************************************************
test_24: ok=3 changed=1 unreachable=0 failed=0 skipped=1 rescued=0 ignored=0

How to include variables with include_vars with the same name without overwriting previous

I am having this let's call it include.yaml
#- name: "Playing with Ansible and Include files"
- hosts: localhost
connection: local
tasks:
- find: paths="./" recurse=yes patterns="test.yaml"
register: file_to_exclude
- debug: var=file_to_exclude.stdout_lines
- name: shell
shell: "find \"$(pwd)\" -name 'test.yaml'"
register: files_from_dirs
- debug: var=files_from_dirs.stdout_lines
- name: Include variable files
include_vars: "{{ item }}"
with_items:
- "{{ files_from_dirs.stdout_lines }}"
- debug: var=files
and 2 ore more test files
./dir1/test.yaml
that contains
files:
- file1
- file2
./dir2/test.yaml
that contains
files:
- file3
- file4
the result is
TASK [Include variable files] ******************************************************************************************
ok: [localhost] => (item=/mnt/c/Users/GFlorinescu/ansible_scripts/ansible/1st/test.yaml)
ok: [localhost] => (item=/mnt/c/Users/GFlorinescu/ansible_scripts/ansible/2nd/test.yaml)
TASK [debug] ***********************************************************************************************************
ok: [localhost] => {
"files": [
"file3",
"file4"
]
}
How can I get all the values in files, at the moment the last included files variable from last file overrides the files from the previous files? Of course without changing the variables names in files test.yaml?
In other words I want files to be:
ok: [localhost] => {
"files": [
"file1",
"file2",
"file3",
"file4"
]
}
To be more specific, I ask for any kind of solution or module, even not official or some github module, I don't want a specific include_vars module solution.
Put the included variables into the dictionaries with unique names. For example, create the names from the index of the loop. Then, iterate the names and concatenate the lists
- command: "find {{ playbook_dir }} -name test.yaml"
register: files_from_dirs
- include_vars:
file: "{{ item }}"
name: "{{ name }}"
loop: "{{ files_from_dirs.stdout_lines }}"
loop_control:
extended: true
vars:
name: "files_{{ ansible_loop.index }}"
- set_fact:
files: "{{ files|d([]) + lookup('vars', item).files }}"
with_varnames: "files_[0-9]+"
- debug:
var: files
give
files:
- file1
- file2
- file3
- file4
Notes:
You have to provide either a path relative to the home directory or an absolute path. See the example below
- command: "echo $PWD"
register: out
- debug:
var: out.stdout
give
out.stdout: /home/admin
For example, when you want to find the files relative to the directory of the playbook
- command: "find {{ playbook_dir }} -name test.yaml"
register: files_from_dirs
- debug:
var: files_from_dirs.stdout_lines
give
files_from_dirs.stdout_lines:
- /export/scratch/tmp8/test-987/dir1/test.yaml
- /export/scratch/tmp8/test-987/dir2/test.yaml
The same is valid for the module find. For example,
- find:
paths: "{{ playbook_dir }}"
recurse: true
patterns: test.yaml
register: files_from_dirs
- debug:
var: files_from_dirs.files|map(attribute='path')|list
give the same result
files_from_dirs.files|map(attribute='path')|list:
- /export/scratch/tmp8/test-987/dir1/test.yaml
- /export/scratch/tmp8/test-987/dir2/test.yaml
Simplify the code and put the declaration of files into the vars. For example, the below declaration gives the same result
files: "{{ query('varnames', 'files_[0-9]+')|
map('extract', hostvars.localhost, 'files')|
flatten }}"
Example of a complete playbook for testing
- hosts: localhost
vars:
files: "{{ query('varnames', 'files_[0-9]+')|
map('extract', hostvars.localhost, 'files')|
flatten }}"
tasks:
- find:
paths: "{{ playbook_dir }}"
recurse: true
patterns: test.yaml
register: files_from_dirs
- include_vars:
file: "{{ item }}"
name: "{{ name }}"
loop: "{{ files_from_dirs.files|map(attribute='path')|list }}"
loop_control:
extended: true
vars:
name: "files_{{ ansible_loop.index }}"
- debug:
var: files
(maybe off-topic, see comments)
Q: "Is there a way to write the path where it was found?"
A: Yes, it is. See the self-explaining example below. Given the inventory
shell> cat hosts
host_1 file_1=alice
host_2 file_2=bob
host_3
the playbook
- hosts: host_1,host_2,host_3
vars:
file_1_list: "{{ hostvars|json_query('*.file_1') }}"
file_2_list: "{{ hostvars|json_query('*.file_2') }}"
file_1_dict: "{{ dict(hostvars|dict2items|
selectattr('value.file_1', 'defined')|
json_query('[].[key, value.file_1]')) }}"
file_1_lis2: "{{ hostvars|dict2items|
selectattr('value.file_1', 'defined')|
json_query('[].{key: key, file_1: value.file_1}') }}"
tasks:
- debug:
msg: |-
file_1_list: {{ file_1_list }}
file_2_list: {{ file_2_list }}
file_1_dict:
{{ file_1_dict|to_nice_yaml|indent(2) }}
file_1_lis2:
{{ file_1_lis2|to_nice_yaml|indent(2) }}
run_once: true
gives
msg: |-
file_1_list: ['alice']
file_2_list: ['bob']
file_1_dict:
host_1: alice
file_1_lis2:
- file_1: alice
key: host_1

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

Splitting a debug msg to extract a certain part of the msg in ansible

So I need to take a particular part of my debug msg that I have in my playbook that goes like
---
- name: extract
shell: grep "ScriptAlias /.*/" /etc/httpd/conf/httpd.conf
register: st
- debug:
msg: "{{ st.stdout_lines | map('trim') | list }}"
and that playbook prints out when ran
ok: [52.61.71.178] => {
"msg": [
"ScriptAlias /cgi-bin/ \"/var/www/cgi-bin/\""
]
}
So what I need to do is put that msg "ScriptAlias /cgi-bin/ \"/var/www/cgi-bin/\""in an array and extract the last element in that which is the "/var/www/cgi-bin/"
What is the best approach in extracting the last element in my msg to where it can get only the "/var/www/cgi-bin/"?
Try as below. Only added regex_replace (Answer From Valdimir) to get exactly what you want.
- hosts: localhost
vars:
lines:
- "ScriptAlias /cgi-bin/ \"/var/www/cgi-bin/\""
tasks:
- debug:
msg: "{{ item.split(' ')[2] | regex_replace('\"', '') }}"
loop: "{{ lines }}"
Output should be exactly as ::
"msg": "/var/www/cgi-bin/"
The split method works fine. Output is abridged.
> ansible-playbook split_test.yml
"msg": "\"/var/www/cgi-bin/\""
split_test.yml
- hosts: localhost
vars:
lines:
- "ScriptAlias /cgi-bin/ \"/var/www/cgi-bin/\""
tasks:
- debug:
msg: "{{ item.split(' ')[2] }}"
loop: "{{ lines }}"

Resources