Ansible - Check if string exists in file with regex - regex

I need to validate that the PermitRootLogin parameter is equal to "no", for example:
PermitRootLogin no
But sometimes between these words there is more than one space. For this reason I use a regex, but apparently I do it wrong. This is the line that seems to be bad:
when: check_config.stdout.find('PermitRootLogin\s+no') != -1
Any idea how to fix this?
- hosts: redhat
tasks:
- name: check file
shell: cat /etc/ssh/sshd_config
register: check_config
- name: compare string
when: check_config.stdout.find('PermitRootLogin\s+no') != -1
debug: msg="this server is ok"

Q: "Validate that the PermitRootLogin parameter is equal to no."
A: Put the below declaration into the vars
match_lines: "{{ check_config.stdout_lines|
map('regex_search', '^\\s*PermitRootLogin\\s+no$')|
select }}"
and test the length of the list
- debug:
msg: this server is OK
when: match_lines|length > 0
Example of a complete playbook for testing
- hosts: localhost
vars:
match_lines: "{{ check_config.stdout_lines|
map('regex_search', '^\\s*PermitRootLogin\\s+no$')|
select }}"
tasks:
- command: cat /etc/ssh/sshd_config
register: check_config
- debug:
var: match_lines
- debug:
msg: This server is OK
when: match_lines|length > 0
gives, for example (abridged)
TASK [debug] *******************************************
ok: [localhost] =>
match_lines:
- PermitRootLogin no
TASK [debug] *******************************************
ok: [localhost] =>
msg: This server is OK
Given the inventory below set hosts -hosts: rehat
shell> cat hosts
[redhat]
test_11
test_12
test_13
The playbook gives, for example (abridged)
TASK [debug] *******************************************
ok: [test_11] =>
match_lines:
- PermitRootLogin no
ok: [test_12] =>
match_lines: []
ok: [test_13] =>
match_lines: []
TASK [debug] *******************************************
skipping: [test_12]
ok: [test_11] =>
msg: This server is OK
skipping: [test_13]
You can use lookup to simplify the task if the play is running at the localhost only. For example, the playbook below gives the same result
- hosts: localhost
tasks:
- debug:
msg: This server is OK
when: match_lines|length > 0
vars:
match_lines: "{{ lookup('file', '/etc/ssh/sshd_config').splitlines()|
map('regex_search', '^\\s*PermitRootLogin\\s+no$')|
select }}"
If you want to put/replace the line in the config use lineinfile. For example,
- lineinfile:
path: /etc/ssh/sshd_config
regexp: '^PermitRootLogin(.*)$'
line: 'PermitRootLogin no'

Related

Access yaml list of dictionaries file with ansible

So I am trying take values from file, let's call it "test.yaml"
file looks like this (sorry for long output, but it is the shortest cut containing all patterns and structure):
---
results:
- failed: false
item: XXX.XX.XX.XX
invocation:
module_args:
validate_certs: false
vm_type: vm
show_tag: false
username: DOMAIN\domain-user
proxy_host:
proxy_port:
show_attribute: false
password: VALUE_SPECIFIED_IN_NO_LOG_PARAMETER
port: XXX
folder:
hostname: XXX.XX.XX.XX
changed: false
virtual_machines:
- ip_address: XXX.XX.XX.XX
mac_address:
- XX:XX:XX:aa:XX:XX
uuid: XXXX-XX-XX-XXXX-XXXXX
guest_fullname: Red Hat Enterprise Linux X (XX-bit)
moid: vm-XXX
folder: "/DOMAIN-INTERXION/vm"
cluster:
attributes: {}
power_state: poweredOn
esxi_hostname: esx.hostname
tags: []
guest_name: VMnameXX
vm_network:
XX:XX:XX:aa:XX:XX:
ipv6:
- XX::XXX:XX:XXXX
ipv4:
- XXX.XX.XX.XX
I would like, for example to have something like:
results.invocation.virtual_machines.ip_address
results.invocation.module_args.user_name
I tried all kind of stuff but it doesn't work :)
last attempt is this:
---
- name: demo how register works
hosts: localhost
tasks:
- name: Include all .json and .jsn files in vars/all and all nested directories (2.3)
include_vars:
file: test.yml
name: vm
- name: debug
debug:
msg: "{{ item.0.item }}"
with_subelements:
- "{{ vm.results }}"
- virtual_machines
register: subelement
following your structure and after fixing some errors:
results.invocation.virtual_machines.ip_address is results[0].virtual_machines[0].ip_address
and
results.invocation.module_args.user_name is results[0].invocation.module_args.username
(results and virtual_machines are arrays, write results[0] or results.0 is same)
so a sample of playbook doing job:
- name: vartest
hosts: localhost
tasks:
- name: Include all .json and .jsn files in vars/all and all nested directories (2.3)
include_vars:
file: test.yml
name: vm
- name: ip
set_fact:
ipadress: "{{ vm.results[0].virtual_machines[0].ip_address }}"
- name: username
set_fact:
username: "{{ vm.results[0].invocation.module_args.username }}"
- name: display
debug:
msg: "ip: {{ ipadress }} and username: {{ username }}"
result:
ok: [localhost] =>
msg: 'ip: XXX.XX.XX.XX and username: DOMAIN\domain-user'

Parsing a list of strings in ansible

I have this list:
ok: [localhost] => {
"ansible_facts": {
"servs": [
"Is active: Yes\nHost Name: northrend\n Ip Address: 192.168.1.12",
"Is active: Yes\nHost Name: pandaria \n IP Address: 192.168.1.30"
]
},
I've been trying to parse this list to extract the value of "Host name" but I just keep on getting the following error:
fatal: [localhost]: FAILED! => {
"msg": "Unexpected templating type error occurred on ({{ servs | regex_search('Host Name: (.)+\n'),multiline=True) }}): expected string or buffer"
}
This is what I tried:
- set_fact:
output: {{ servs | regex_search('Host Name: (.)+\n'), multiline=True) }}
How do you properly parse the contents of this list?
The list servs is nearly valid YAML. Only the indentation of the attribute 'Ip Address' is wrong. One space shifted to the right. If you manage to get rid of this space, e.g.
servs:
- "Is active: Yes\nHost Name: northrend\nIp Address: 192.168.1.12"
- "Is active: Yes\nHost Name: pandaria \nIP Address: 192.168.1.30"
the situation would be trivial. Simply convert the list of strings to a list of dictionaries, e.g.
- set_fact:
servs_list: "{{ servs_list|d([]) + [item|from_yaml] }}"
loop: "{{ servs }}"
gives
servs_list:
- Host Name: northrend
Ip Address: 192.168.1.12
Is active: true
- Host Name: pandaria
IP Address: 192.168.1.30
Is active: true
If you can't fix the indentation parse the data on your own. The task below gives the same result
- set_fact:
servs_list: "{{ servs_list|d([]) + [item|
regex_replace(regex, replace)|
from_yaml] }}"
loop: "{{ servs }}"
vars:
regex: '\n '
replace: '\n'
Get the output
- set_fact:
output: "{{ servs_list|map(attribute='Host Name')|list }}"
gives
output:
- northrend
- Pandaria
You have multiple errors here:
regex_search works on strings, not lists. So you need to apply it to each list element.
Your regex creates one group for each character in your hostname. The group should be (.+).
You are returning the whole match (including Host Name:) and not just the name itself. You need to specify which matching group to return.
I guess the list is from the output of some script or in some file. It would be a lot better to have it as json or yaml and then use from_json or from_yaml.
If you want to keep it like that, you need to do this:
- hosts: all
vars:
servs:
- |
Is active: Yes
Host Name: northrend
Ip Address: 192.168.1.12
- |
Is active: Yes
Host Name: pandaria
IP Address: 192.168.1.30
tasks:
- debug:
msg: "{{ servs }}"
- set_fact:
output: "{{ servs | map('regex_search', 'Host Name: (.+)\n', '\\1') | list }}"
- debug:
msg: "{{ output }}"
This is the output:
TASK [debug] ************
ok: [local] => {
"msg": [
"Is active: Yes\nHost Name: northrend\nIp Address: 192.168.1.12\n",
"Is active: Yes\nHost Name: pandaria\nIP Address: 192.168.1.30\n"
]
}
TASK [set_fact] ***************
ok: [local]
TASK [debug] ************
ok: [local] => {
"msg": [
[
"northrend"
],
[
"pandaria"
]
]
}

ansible jinja2 template file creation based on input values

i want to create a file application.properties file and copy it over to specific location. im using template module for same. but i want to create content of the file based on comma separated input ip addresses count/values
the content of the file should is below.
conf_pg_hba_replication.connection=host replication oracle {{IP1}}/32 trust\nhost replication oracle {{IP2}}/32 trust\nhost replication oracle {{IP3}}/32.............
so i want my file to be created with dynamic content based on comma separated input ip address value.
if my input ip value is 127.0.0.1,123.123.123.123
file content should be
conf_pg_hba_replication.connection=host replication oracle 127.0.0.1/32 trust\nhost replication oracle 123.123.123.123/32 trust
so likewise i need to create contents of file.
please help me on this.
---
- name: pp
hosts: localhost
tasks:
- name: pp
template:
src: pp.j2
dest: pp.properties
newline_sequence: \n
-bash-4.2$ cat pp.j2
conf_pg_hba_replication.connection=host replication oracle {{slave_ip}}/32 trust
i pass ips list through a variable to ansible playbook like below
ansible-playbook postgres.yml -e "ips_list=ip1,ip2,ip3"
You are passing a coma separated ip list as an extra var on the ansible command line.
To acheive your requirement you need to:
transform the string containing the coma separated list to a real list with the split function.
loop over you list in your template to output the result.
This can actually be done in a single task. Given the following templates/pp.j2...
{% for slave_ip in ip_list %}
conf_pg_hba_replication.connection=host replication oracle {{ slave_ip }}/32 trust
{% endfor %}
and the following playbook...
---
- name: template our pp file
hosts: localhost
gather_facts: false
tasks:
- name: pp
template:
src: pp.j2
dest: pp.properties
vars:
ip_list: "{{ ip_list_raw.split(',') }}"
that you call like this...
ansible-playbook test.yml -e "ip_list_raw=127.0.0.1,1.2.3.4,5.6.7.8"
you get the following result
$ cat pp.properties
conf_pg_hba_replication.connection=host replication oracle 127.0.0.1/32 trust
conf_pg_hba_replication.connection=host replication oracle 1.2.3.4/32 trust
conf_pg_hba_replication.connection=host replication oracle 5.6.7.8/32 trust
Note that you can also drop the vars declaration with the split in the template task by passing directly the list of ips as a json array on the command line
ansible-playbook test.yml -e '{"ip_list":["127.0.0.1","1.2.3.4","5.6.7.8"]}'
Or even load them from an external yaml file e.g. my_ip_list.yml
---
ip_list:
- 127.0.0.1
- 1.2.3.4
- 5.6.7.8
like this:
ansible-playbook test.yml -e #my_ip_list.yml'
---
- name: check for postgresql standby
command: "{{ service }} postgres-check-standby"
ignore_errors: yes
register: check_standby
- set_fact:
is_standby: "N"
when:
- '"Error: Cannot get status of postgres server" in check_standby.stdout or "Error: Postgres-Server reports being" in check_standby.stdout'
- '" pg_last_xlog_receive_location \n-------------------------------\n \n(1 row)" in check_standby.stdout'
- set_fact:
is_standby: "Y"
when:
- '"postgres-server is slave/standby" in check_standby.stdout or "Error: Cannot get status of postgres server" in check_standby.stdout'
- '" pg_last_xlog_receive_location \n-------------------------------\n \n(1 row)" not in check_standby.stdout'
- set_fact:
is_standby: "D"
when:
- '"ERROR: postgres server is down or does not seem be standby" in check_standby.stdout'
- '"Is the server running locally and accepting" in check_standby.stderr'
- name: print if standby
command: echo "postgres-server is slave/standby"
when: is_standby == "Y"
- name: print if not standby
command: echo "postgres-server is not slave/standby"
when: is_standby == "N"
- name: print if component is down
command: echo "postgresql component is down"
when: is_standby == "D"
---
- name: check for postgresql master
command: "{{ service }} postgres-check-master"
ignore_errors: yes
register: check_master
- set_fact:
is_master: "Y"
when:
- '" pg_current_xlog_location \n--------------------------\n \n(1 row)" not in check_master.stdout'
- '"postgres-server is master" in check_master.stdout or "Cannot get status of postgres server" in check_master.stdout'
- set_fact:
is_master: "N"
when:
- '"postgres server is down or does not seem be" in check_master.stdout'
- '"ERROR: recovery is in progress" in check_master.stderr'
- set_fact:
is_master: "D"
when:
- '"postgres server is down or does not seem be" in check_master.stdout'
- '"Is the server running locally and accepting" in check_master.stderr'
- name: print if master
command: echo "postgres-server is master"
when: is_master == "Y"
- name: print if not master
command: echo "postgres-server is not master"
when: is_master == "N"
- name: print if component is down
command: echo "postgresql component is down"
when: is_master == "D"
---
- name: check postgresql status
command: "{{ service }} status"
ignore_errors: yes
register: postgresql_status
- name: start if its down
command: "{{ service }} start"
when:
- 'postgresql_status is failed or postgresql_status.rc != 0 or "apigee-service: apigee-postgresql: OK" not in postgresql_status.stderr'
- name: validate it is a standby node
include: check_standby.yml
- fail:
msg: postgres-server is not standby or down - please check"
when: is_standby != "Y"
- name: get ip address of old_master
shell: host {{OLD_MASTER_FQDN}} | awk -F"has address " '{print$2}'
ignore_errors: yes
register: old_master_ip
- name: get no of octet in ip address
shell: echo {{old_master_ip.stdout}} | sed s'/\./ /g' | wc -w
ignore_errors: yes
register: ip_octet_count
- name: echo old_master_ip
command: echo Old Master Node Ip address:- {{old_master_ip.stdout}}
when: old_master_ip.stdout != "" and ip_octet_count.stdout == "4"
- name: fail if unable to get ip address
fail:
msg: failed to get the ip address, please check and enter FQDN of old master postgres node
when: old_master_ip.stdout == "" or ip_octet_count.stdout != "4"
- name: Switching standby to master
shell: "{{ service}} promote-standby-to-master {{old_master_ip.stdout}}"
- name: validate master post swap
include: check_master.yml
- fail:
msg: postgresql is not master. please check microservice logs for error details.
when: is_master != "Y"
- name: backup existing file
command: "mv {{properties_file}} {{properties_file}}_backup{{ansible_date_time.date}}"
when: postgres_properties_file.stat.exists
- name: download properties file from Bitbucket
shell: "curl -k -v -u {{ username }}:{{ passwd }} {{POSTGRESQL_PROPERTIES_BB_URL}}?raw -o {{properties_file}}"
args:
warn: no
- name: restart postgresql component
command: "{{ service }} restart"
- name: print Outcome
command: echo "Successfully converted postgresql slave to master"
---
- name: check whether node is master node
include: check_master.yml
- name: bring down node if master is up and running
command: "{{ service }} stop"
when: is_master != "D"
- name: get ip address of new_master
shell: host {{NEW_MASTER_FQDN}} | awk -F"has address " '{print$2}'
ignore_errors: yes
register: new_master_ip
- name: get slave ip
shell: hostname -i
ignore_errors: yes
register: slave_ip
- name: get no of octet in slave ip address
shell: echo {{slave_ip.stdout}} | sed s'/\./ /g' | wc -w
ignore_errors: yes
register: slave_ip_octet_count
- name: get no of octet in new master ip address
shell: echo {{new_master_ip.stdout}} | sed s'/\./ /g' | wc -w
ignore_errors: yes
register: ip_octet_count_m
- name: echo new_master_ip and slave ip
command: "{{ item }}"
with_items:
- echo New Master Node Ip address:- {{new_master_ip.stdout}}
- echo Slave Ip address:- {{slave_ip.stdout}}
when: new_master_ip.stdout != "" and ip_octet_count_m.stdout == "4" and slave_ip.stdout != "" and slave_ip_octet_count.stdout == "4"
- name: fail if unable to get ip address
fail:
msg: failed to get the ip addresses, please check and enter FQDN of postgresql node
when: new_master_ip.stdout == "" or ip_octet_count_m.stdout != "4" or slave_ip.stdout == "" or slave_ip_octet_count.stdout != "4"
- name: create temp replication config file
copy:
dest: /tmp/setup_replication.txt
content: |
PG_MASTER={{new_master_ip.stdout}}
PG_STANDBY={{slave_ip.stdout}}
mode: 0750
- name: comment out the conf_pg_hba_replication.connection property in postgresql properties file
replace:
path: "{{properties_file}}"
regexp: 'conf_pg_hba_replication.connection='
replace: '#conf_pg_hba_replication.connection='
backup: yes
when: postgres_properties_file.stat.exists
- name: remove data folder
file:
path: /opt/apigee/data/apigee-postgresql
state: absent
- name: perform postgresql standby replication
shell: nohup {{service}} setup-replication-on-standby -f /tmp/setup_replication.txt >/tmp/standby_replication.log &
register: nohup_rep
- name: get PID of replication progress
shell: ps -eaf|grep -i setup-replication-on-standby|grep -v grep
ignore_errors: true
register: PID
- name: get PID count
shell: ps -eaf|grep -i setup-replication-on-standby|grep -v grep|wc -l
ignore_errors: true
register: PID_COUNT
- fail:
msg: There is an issue with replication - Please check the logs.
when: PID_COUNT.stdout == "0"
- name: print PID of replication process
command: "{{ item }}"
with_items:
- "echo {{nohup_rep.cmd}}"
- "echo {{PID.stdout}}"

Access element of list as dict

I'm trying to access the elements of a list, but whenever I try to access it's elements they are treated as text (see the debug output).
How can I loop over the elements of content[0]?
ansible version: 2.5.1
playbook.yml:
- hosts: localhost
gather_facts: no
vars:
content: []
tasks:
- name: Call the Consul API to get all registered services
uri:
url: "https://{{API_URL}}/v1/health/service/{{item}}?Status=critical"
headers:
Authorization: "Bearer {{TOKEN}}"
method: GET
return_content: yes
status_code: 200
body_format: json
force: yes
register: r
with_items:
- service1
- service2
- set_fact:
content: "{{ lookup('flattened', r | json_query('results[*].content')) }}"
- debug:
msg: "{{ content[0][0] | type_debug }}"
- debug:
msg: "{{ item | type_debug }}"
with_items: "{{ content[0] }}"
debug output:
TASK [debug]
ok: [127.0.0.1] => {
"msg": "dict"
}
TASK [debug]
ok: [127.0.0.1] => (item=None) => {
"msg": "AnsibleUnsafeText"
}
I'm hoping this question and answer will help you. I think the approach you are taking is the problem.

Extract substring from variable in Ansible

Edited:
I wrote this playbook but it does not show the extracted variable:
---
- hosts: fppc
gather_facts: false
remote_user: xyz
connection: local
tasks:
- name: N1
ios_command:
commands:
- sh run | i bann
register: sr
- debug: msg="{{ sr.stdout}}"
- set_fact:
rid: "{{ sr.stdout | regex_search('.*ID: (..)') }}"
- debug: msg="{{ rid }}"
Execution:
ansible#Ansible:~$ ansible-playbook pb1.yml
PLAY [fppc] *************************************************************************
TASK [N1] ***************************************************************************
ok: [192.168.250.161]
TASK [debug] ************************************************************************
ok: [192.168.250.161] => {
"msg": [
"banner login ^CID: A4"
]
}
TASK [set_fact] *********************************************************************
fatal: [192.168.250.161]: FAILED! => {"failed": true, "msg": "Unexpected templating type error occurred on ({{ sr.stdout | regex_search('.*ID: (..)') }}): expected string or buffer"}
to retry, use: --limit #/home/ansible/pb1.retry
PLAY RECAP **************************************************************************
192.168.250.161 : ok=2 changed=0 unreachable=0 failed=1
ansible#Ansible:~$
I found the solution:
---
- hosts: fppc
gather_facts: false
remote_user: xyz
connection: local
tasks:
- name: N1
ios_command:
commands:
- sh run
register: sr
- set_fact:
temp: "{{ sr.stdout_lines | join }}"
- set_fact:
rid: "{{ temp | regex_replace('.*ID: (..).*', '\\1') }}"
- debug: msg="{{ rid }}"
Execution:
ansible#Ansible:~$ ansible-playbook pb1.yml
PLAY [fppc] ********************************************************************
TASK [N1] **********************************************************************
ok: [192.168.250.161]
TASK [set_fact] ****************************************************************
ok: [192.168.250.161]
TASK [set_fact] ****************************************************************
ok: [192.168.250.161]
TASK [debug] *******************************************************************
ok: [192.168.250.161] => {
"msg": "A4"
}
PLAY RECAP *********************************************************************
192.168.250.161 : ok=4 changed=0 unreachable=0 failed=0
Other solution, more logical:
---
- hosts: fppc
gather_facts: false
remote_user: xyz
connection: local
tasks:
- name: N1
ios_command:
commands:
- sh run
register: sr
- set_fact:
rid: "{{ sr.stdout | regex_search('(?<=ID:)\\s+\\S+', multiline=True) | trim }}"
- debug: msg="{{ rid }}"
Execution:
ansible#Ansible:~$ ansible-playbook pb1.yml
PLAY [fppc] ********************************************************************
TASK [N1] **********************************************************************
ok: [192.168.250.161]
TASK [set_fact] ****************************************************************
ok: [192.168.250.161]
TASK [debug] *******************************************************************
ok: [192.168.250.161] => {
"msg": "A4"
}
PLAY RECAP *********************************************************************
192.168.250.161 : ok=3 changed=0 unreachable=0 failed=0