Ansible how to split string in newline - ansible

I am running in ansible playbook with:
--extra-vars "log_path=/var/logs/a*.log,/repo/log-events-a*.json,repo/user1/log-events-b*.json"
as comma separated lines, I want the output in filebeat.yml file as
paths:
- /var/logs/a*.log
- /repo/log-events-a*.json
- /repo/user1/log-events-b*.json
I am using jinja2 for filebeat.yml
paths:
- {{ log_path }}
And my ansible file testconfigure.yml is
- hosts: localhost
gather_facts: no
vars:
log_path: "{{ logpath.replace(',', '\n-')}}"
tasks:
- name: a jija test
template:
src: /repo/filebeat/filebeat.j2
dest : /repo/filebeat/filebeat.yml
I am getting the output in filebeat.yml file as:
paths:
- /var/logs/*.log,/repo/log-events-a*.json,/repo/user1/log-events-b*.json
I also tried logpath: "{{ logpath | regex_replace(',', '\n-') }" in my playbook, but still getting same output.
How should I try it?

create a j2 file :
paths:
{% for log in log_path %}
- {{ log }}
{% endfor %}
playbook:
- hosts: localhost
vars:
log_path: "{{ logpath.split(',') }}"
tasks:
- name: templating
template:
src: filebeat.j2
dest: filebeat.yml
and the command to call:
ansible-playbook yourplaybook.yml --extra-vars "logpath=/var/logs/a*.log,/repo/log-events-a*.json,repo/user1/log-events-b*.json
result:
paths:
- /var/logs/a*.log
- /repo/log-events-a*.json
- repo/user1/log-events-b*.json
if you just want to create a var file, no need to template:
- name: create var file
copy:
content: "{{ log_path | to_nice_yaml }}"
dest: filebeat.yml
result:
paths:
- /var/logs/a*.log
- /repo/log-events-a*.json
- repo/user1/log-events-b*.json

Related

Create Local File With Ansible Template From Variables

I'm running an ansible playbook against a number of ec2 instances to check if a directory exists.
---
- hosts: all
become: true
tasks:
- name: Check if foo is installed
stat:
path:
/etc/foo
register: path
- debug: msg="{{path.stat.exists}}"
And I would like to generate a localfile that lists the private IP addresses of the ec2 instances and states whether or not the directory foo does exist.
I can get the private IP addresses of the instances with this task
- name: Get info from remote
shell: curl http://169.254.169.254/latest/meta-data/local-ipv4
register: bar
- debug: msg="{{bar.stdout}}"
How do I create a local file with content
IP address: 10.100.0.151 directory foo - false
IP address: 10.100.0.152 directory foo - true
I've tried adding a block for this as such
- hosts: localhost
become: false
vars:
installed: "{{bar.stdout}}"
status: "{{path.stat.exists}}"
local_file: "./Report.txt"
tasks:
- name: Create local file with info
copy:
dest: "{{ local_file }}"
content: |
"IP address {{ installed }} foo - {{ status }}"
But it doesn't look like I can read values of variables from earlier steps.
What am I doing wrong please?
A similar question has been answered here.
Basically what you want is to reference it through the host var variable.
This should work.
- hosts: localhost
become: false
vars:
local_file: "./Report.txt"
tasks:
- name: Create local file with info
lineinfile:
path: "{{ local_file }}"
line:
"IP Address: {{ hostvars[item]['bar'].stdout }} - Installed: {{ hostvars[item]['path'].stat.exists }}"
with_items: "{{ query('inventory_hostnames', 'all') }}"
And this should populate your local ./Report.txt file, with the info you need.
I've used the ec2_metadata_facts module to get the IP address us ingansible_ec2_local_ipv4
I've also created the directory /tmp/testdir on the second host.
- hosts: test_hosts
gather_facts: no
vars:
directory_name: /tmp/testdir
tasks:
- ec2_metadata_facts:
- name: check if directory '{{ directory_name }}' exsists
stat:
path: "{{ directory_name }}"
register: path
# I make the outputfile empty
# because the module lineinfile(as I know) can't overwrite a file
# but appends line to the old content
- name: create empty output file
copy:
content: ""
dest: outputfile
delegate_to: localhost
- name: write output to outputfile
lineinfile:
dest: outputfile
line: "IP Address: {{ ansible_ec2_local_ipv4 }} {{ directory_name }} - {{ path.stat.exists }}"
state: present
with_items: "{{ groups.all }}"
# with_items: "{{ ansible_play_hosts }}" can also be used here
delegate_to: localhost
The outputfile looks like:
IP Address: xxx.xx.x.133 /tmp/testdir - False
IP Address: xxx.xx.x.45 /tmp/testdir - True

Place file contents in email body in Ansible

I have a playbook that checks hard drive space on a group of servers, sends out an email, and it attaches a file containing the output contents. Is there a way to place the contents of the file in the body itself? I would like the file contents to be able to be seen at a glance in the email. The content would be the registered variable, result.
The current tasks:
---
- name: Check for output file
stat:
path: /tmp/ansible_output.txt
register: stat_result
delegate_to: localhost
- name: Create file if it does not exist
file:
path: /tmp/ansible_output.txt
state: touch
mode: '0666'
when: stat_result.stat.exists == False
delegate_to: localhost
- name: Check hard drive info
become: yes
become_user: root
shell: cat /etc/hostname; echo; df -h | egrep 'Filesystem|/dev/sd'
register: result
- debug: var=result.stdout_lines
- local_action: lineinfile line={{ result.stdout_lines | to_nice_json }} dest=/tmp/ansible_output.txt
- name: Email Result
mail:
host: some_email_host
port: some_port_number
username: my_username
password: my_password
to:
- first_email_address
- second_email_address
- third_email_address
from: some_email_address
subject: My Subject
body: <syntax for file contents in body here> <--- What is the syntax?
attach:
/tmp/ansible_output.txt
run_once: true
delegate_to: localhost
- name: Remove Output File
file:
path: /tmp/ansible_output.txt
state: absent
run_once: true
delegate_to: localhost
Edit: I tried
body: "{{ result.stdout_lines | to_nice_json }}"
but it only sends me the output of the first host in the group.
Ok, I figured it out. I created a directory, files, and sent the output to a file in that directory using the {{ role_path }} variable. In the body portion of the email task, I used the lookup plugin to grab the contents of the file.
Here is the updated playbook with the original lines commented out:
---
- name: Check for output file
stat:
#path: /tmp/ansible_output.txt
path: "{{ role_path }}/files/ansible_output.txt"
register: stat_result
delegate_to: localhost
- name: Create file if it does not exist
file:
#path: /tmp/ansible_output.txt
path: "{{ role_path }}/files/ansible_output.txt"
state: touch
mode: '0666'
when: stat_result.stat.exists == False
delegate_to: localhost
- name: Check hard drive info
become: yes
become_user: root
shell: cat /etc/hostname; echo; df -h | egrep 'Filesystem|/dev/sd'
register: result
- debug: var=result.stdout_lines
#- local_action: lineinfile line={{ result.stdout_lines | to_nice_json }} dest=/tmp/ansible_output.txt
- local_action: lineinfile line={{ result.stdout_lines | to_nice_json }} dest="{{ role_path }}/files/ansible_output.txt"
- name: Email Result
mail:
host: some_email_host
port: some_port_number
username: my_username
password: my_password
to:
- first_email
- second_email
- third_email
from: some_email_address
subject: Ansible Disk Space Check Result
#body: "{{ result.stdout_lines | to_nice_json }}"
body: "{{ lookup('file', '{{ role_path }}/files/ansible_output.txt') }}"
#attach:
#/tmp/ansible_output.txt
attach:
"{{ role_path }}/files/ansible_output.txt"
run_once: true
delegate_to: localhost
- name: Remove Output File
file:
#path: /tmp/ansible_output.txt
path: "{{ role_path }}/files/ansible_output.txt"
state: absent
run_once: true
delegate_to: localhost
Now, my email contains the attachment, as well as the contents in the body, and I didn't have to change much in the playbook. :-)

Replace host variables in Jijnja template file in ansible

I need to replace a file with the variables defined in the host file.
Following is the host variables defined in "host_vars/abc.1234.com"
env: acc
abcserverName:
- name: abc1
- name: abc2
The playbook file has the following contents
- hosts: "abc.1234.com"
become: yes
tasks:
- name: deploy abc control file
template:
src: abc-control.j2
dest: /etc/init.d/{{ env }}-{{ item.name }}
with_items:
- "{{ abcservername }}"
- name: start abcserver
command: /etc/init.d/control-{{ env }}-{{ item.name }} start
with_items:
- "{{ abcserverName }}"
This will copy 2 files in init.d, which is the following:
/etc/init.d/control-acc-abc1
/etc/init.d/control-acc-abc2
Requirement:
Inside each of the above control files, I also need to get the correct "abcserverName"
For example in "/etc/init.d/control-acc-abc1", I want
SERVER_NAME=abc1
and in For example in "/etc/init.d/control-acc-abc2"
SERVER_NAME=abc2
I don't have much knowledge about jinja templates and google shows me complex examples. Any help to achieve this is appreciated.
Take this jinja2 code for example:
SERVER_NAME={{ server_name }}
server_name is not actually defined, and to use it you will need to pass it as a variable to your template.
Using your task:
- name: deploy abc control file
template:
src: abc-control.j2
dest: /etc/init.d/{{ env }}-{{ item.name }}
with_items:
- "{{ abcservername }}"
vars:
server_name: {{ item.name }}
Now "control-acc-abc1" will have server_name set to abc1 and "control-acc-abc2" will have server_name set to abc2

How to extract the output from stdout.lines in ansible

---
- name: Mikrotik info
hosts: mikrotik
connection: network_cli
remote_user: root
gather_facts: false
tasks:
- name: show info
routeros_command:
commands: /system routerboard print
register: rb_info
- name: Debug info
debug:
msg: "{{ rb_info.stdout_lines }}"
Output:
routerboard: yes
model: 751G-2HnD
serial-number: 3A6502B2A2E7
firmware-type: ar7240
factory-firmware: 3.0
current-firmware: 6.42.3
upgrade-firmware: 6.43.4
I need to filter it for "upgrade-firmware" string and get output like this:
upgrade-firmware: 6.43.4
I should use regex_replace? Or I can use grep or something like that?
Any thoughts are greatly appreciated.
Thank you
(update)
Use from_yaml and combine a dictionary. For example
- set_fact:
minfo: "{{ minfo|default({})|combine(item|from_yaml) }}"
loop: "{{ rb_info.stdout_lines }}"
- debug:
var: minfo['upgrade-firmware']
give
minfo['upgrade-firmware']: 6.43.4
(for the record)
Robust solution is to write the data to template and include_vars. The tasks below
- tempfile:
register: tempfile
- template:
src: minfo.j2
dest: "{{ tempfile.path }}"
- include_vars:
file: "{{ tempfile.path }}"
name: minfo
- debug:
var: minfo
with the template
shell> cat minfo.j2
{% for item in rb_info.stdout_lines %}
{{ item }}
{% endfor %}
should give
"minfo": {
"current-firmware": "6.42.3",
"factory-firmware": 3.0,
"firmware-type": "ar7240",
"model": "751G-2HnD",
"routerboard": true,
"serial-number": "3A6502B2A2E7",
"upgrade-firmware": "6.43.4"
}
The tasks below creates variable upgrade_firmware
- set_fact:
upgrade_firmware: "{{ item.split(':').1|trim }}"
loop: "{{ rb_info.stdout_lines|map('quote')|map('trim')|list }}"
when: item is search('^upgrade-firmware')
- debug:
var: upgrade_firmware
It is possible to put all the parameters into the dictionary
- set_fact:
minfo: "{{ minfo|default({})|
combine({item.split(':').0: item.split(':').1|trim}) }}"
loop: "{{ rb_info.stdout_lines|map('quote')|map('trim')|list }}"
- debug:
var: minfo['upgrade-firmware']

Ansible access same variables from multiple Json files

I have multiple .json files on local host where I place my playbook:
json-file-path/{{ testName }}.json
{{ testName }}.json are: testA.json, testB.json, testC.json ... etc.
All .json files have same keys with different values like this:
json-file-path/testA.json:
{
“a_key”: “a_value1”
“b_key”: “b_value1”
}
json-file-path/testB.json:
{
“a_key”: “a_value2”
“b_key”: “b_value2”
}
json-file-path/testC.json:
{
“a_key”: “a_value3”
“b_key”: “b_value3”
}
.....
I need to access the key-value variables from all .json files and if the values meet some condition, I will perform some task in target host. For example, I have:
a_value1=3
a_value2=4
a_value3=1
I go through my .json file one by one, if a_key[value]>3, I will copy this .json file to target host, otherwise skip the task. In this case, I will only copy testC.json to target host.
How would I achieve this? I was thinking of re-constructing my .json files using {{ testName }} as dynamic key of dict like this:
{
“testName”: “testA”
{
“a_key”: “a_value1”
“b_key”: “b_value1”
}
So I can access my variable as {{ testName}}.a_key. So far I haven’t been able to achieve this.
I have tried the following in my playbook:
—-
- host: localhost
tasks:
- name: construct json files
vars:
my_vars:
a_key: “{{ a_value }}”
b_key: “{{ b_value }}”
with_dict: “{{ testName }}”
copy:
content: “{{ my_vars | to_nice_json }}”
dest: /json-file-path/{{ testName }}.json
My updated playbook are:
/mypath/tmp/include.yaml:
—-
- hosts: remote_hostName
tasks:
- name: load json files
set_fact:
json_data: “{{ lookup(‘file’, item) | from_json }}”
- name: copy json file if condition meets
copy:
src: “{{ item }}”
dest: “{{ /remote_host_path/tmp}}/{{item | basename }}”
delegate_to: “{{ remote_hostName }}”
when: json_data.a_key|int>5
/mypath/test.yml:
—-
- hosts: localhost
vars:
local_src_ dir: /mypath/tmp
remote_host: remote_hostName
remote_dest_dir: /remote_host_path/tmp
tasks:
- name: looping
include: include.yaml
with_fileglob:
- “{{ local_src_dir }}/*json”
All json files on localhost under /mypath/tmp/.
Latest version of playbook. It is working now:
/mypath/tmp/include.yaml:
—-
- name: loafing json flies
include_vars:
file: “{{ item }}”
name: json_data
- name: copy json file to remote if condition meets
copy:
src: “{{ item }}”
dest: ‘/remote_host_path/tmp/{{item | basename}}’
delegate_to: “{{ remote_host }}”
when: json_data.a_key > 5
/mypath/test.yml:
—-
- hosts: localhost
vars:
local_src_dir: /mypath/tmp
remote_host: remote_hostName
remote_dest_dir: /remote_host_path/tmp
tasks:
- name: looping json files
include: include.yaml
with_fileglob:
- “{{ local_src_dir }}”/*json”
I am hoping that I have understood your requirements correctly, and that this helps move you forward.
Fundamentally, you can load each of the JSON files so you can query the values as native Ansible variables. Therefore you can loop through all the files, read each one, compare the value you are interested in and then conditionally copy to your remote host via a delegated task. Therefore, give this a try:
Create an include file include.yaml:
---
# 'item' contains a path to a local JSON file on each pass of the loop
- name: Load the json file
set_fact:
json_data: "{{ lookup('file', item) | from_json }}"
- name: Delegate a copy task to the remote host conditionally
copy:
src: "{{ item }}"
dest: "{{ remote_dest_dir }}/{{ item | basename }}"
delegate_to: "{{ remote_host }}"
when: json_data.a_key > value_threshold
then in your playbook:
---
- hosts: localhost
connection: local
# Set some example vars, tho these could be placed in a variety of places
vars:
local_src_dir: /some/local/path
remote_host: <some_inventory_hostname>
remote_dest_dir: /some/remote/path
value_threshold: 3
tasks:
- name: Loop through all *json files, passing matches to include.yaml
include: include.yaml
loop: "{{ lookup('fileglob', local_src_dir + '/*json').split(',') }}"
Note: As you are running an old version of Ansible, you may need older alternate syntax for all of this to work:
In your include file:
- name: Load the json file
set_fact:
include_vars: "{{ item }}"
- name: Delegate a copy task to the remote host conditionally
copy:
src: "{{ item }}"
dest: "{{ remote_dest_dir }}/{{ item | basename }}"
delegate_to: "{{ remote_host }}"
when: a_key > value_threshold
and in your playbook:
- name: Loop through all *json files, passing matches to include.yaml
include: include.yaml
with_fileglob:
- "{{ local_src_dir }}/*json"

Resources