How can I copy multiple files from inventory_hostname in Ansible? - ansible

I want to log my config files, using inventory_hostname variable but it's only considering the first host in my inventory file and copying its config to all the other hosts. (Note: All 3 configs are different)
My Task:
- name: Logging Output
blockinfile:
path: "./output.log"
block: |
Config files
------------------------------
{% for host_name in ansible_play_hosts_all %}
{{ host_name }}
{{ inventory_hostname }}.cfg
{% endfor %}
My inventory file:
[junos]
junos-1
junos-2
junos-3
[junos:vars]
ansible_network_os=junos
ansible_connection=netconf
My config files:
junos-1.cfg
junos-2.cfg
junos-3.cfg
which were created using:
- name: Generate Config
template:
src: ./generate_config.j2
dest: ./{{ inventory_hostname }}.cfg
Can someone please suggest how can I copy all my configs for their respective hosts or if there is a way I can loop within inventory_hostname in the same task to fetch my configs?

Related

Ansible run shell module on multiple host's and redirect output to 1 file

I need to run shell module on all hosts group and copy the register variable to a file on any server.
NOTE : I don't want to copy the results in my local i need it on server
- name: date.
shell: cat /ngs/app/user/test
register: date_res
changed_when: false
- debug:
msg: "{{ ansible_play_hosts | map('extract', hostvars, 'date_res') | map(attribute='stdout') | list }}"
run_once: yes
- name: copy bulk output
copy:
content: "{{ allhost_out.stdout }}"
dest: "/ngs/app/{{ app_user }}/test"
Jinja2 loop can be helpful for your case.
Tested on ansible [core 2.13.3]
- name: date.
shell: cat /ngs/app/user/test
register: date_res
changed_when: false
- name: copy bulk output
copy:
content: |
{% for host in vars['play_hosts'] %}
{{ host }} {{ hostvars[host].date_res.stdout }}
{% endfor %}
dest: "/ngs/app/{{ app_user }}/bldall"
when: inventory_hostname == host-001.example.com
On host-001.example.com placed a file contains: host1 file content.
On host-002.example.com placed a file contains: host2 file content.
Output on host specified in the copy task:
host-001.example.com host1 file content
host-002.example.com host2 file content

can I run a shell command in jinja2 template in ansible

This is a playbook that connects with all the servers in my inventory file, and makes a note of the server ip and mount point information of hosts where mount point usage exceeds 80% and it writes to a text file on the localhost (ansible-controller).
- hosts: all
tasks:
- shell:
cmd: df -h | sed 's/%//g' | awk '$5 > 80 {if (NR > 1) print $5"%",$6}'
register: disk_stat
- debug:
var: disk_stat
- file:
path: /home/app/space_report_{{ td }}.txt
state: touch
run_once: true
delegate_to: localhost
- shell: echo -e "{{ ansible_host }} '\n' {{ disk_stat.stdout_lines| to_nice_yaml }}" >> /home/thor/space_report_{{ td }}.txt
args:
executable: /bin/bash
delegate_to: localhost
I was wondering if I could create a jinja2 template and bring the playbook down to one task. I am stuck at integrating a shell command inside the jinja2 template and I am not sure if it is possible. Please advise.
- hosts: all
tasks:
- template:
src: monitor.txt.j2
dest: /home/app/playbooks/monitor.txt
delegate_to: localhost
monitor.txt.j2
{% for host in groups['all'] %}
{{ hostvars[host].ansible_host }}
--shell command--
{% endfor %}
As I say in my comment under your question, while it is possible to use shell or command modules, Ansible is a configuration / automation tool, so it's better to forget your shell coding / logic to use native ansible functionalities, that'll ease the tasks / playbook writing.
For instance, it's no needed to do a df because ansible when connecting will gather facts about the target, including devices and their capacity and current usage so you can use that directly.
For the jinja question, you can use the module copy and pass directly jinja code in the option content on this module:
- name: Trigger a tasks on hosts to gather facts
hosts: all
tasks:
- copy:
dest: /home/app/playbooks/monitor.txt
content: |
{% for host in groups['all'] %}
{% for dev in hostvars[host].ansible_mounts %}
{% if (dev.block_used / dev.block_total * 100 ) > 80 %} {{ dev.block_used / dev.block_total * 100 }} {{ dev.mount }} {% endif %}
{% endfor %}
{% endfor %}
run_once: true
delegate_to: localhost

Ansible Jinja2 template for loop

I have two linux servers:
- server1: ip: 10.241.55.6, hostname: server1
- server2: ip: 10.242.55.7, hostname: server2
I have created an ansible inventory file named servers with the content bellow:
[IC]
10.241.55.6
10.241.55.7
Now I have created this jinja2 inventory template file: test.j2 with this content:
[IC]
{% for hostip in groups['IC'] %}
{% if hostip == ansible_default_ipv4.address %}
{{ ansible_default_ipv4.address }} default_hostname={{ ansible_nodename }}
{{ ansible_default_ipv4.address }} default_hostname={{ ansible_nodename }}
{% endif %}
{% endfor %}
And I'm running this ansible playbook:
---
- name: Generate portal inventory file
hosts: all
tasks:
- name: Generate inventory
delegate_to: localhost
template:
src: inventory/test.j2
dest: inventory/test
The command is: ansible-playbook -i inventory/servers generate-inventory.yml
The final goal is that ansible connects to each of the servers from the inventory files and then based on the jinja2 inventory template, it creates a new inventory file with this format:
[IC]
10.241.55.6 default_hostname=hostname_of_the_server_with_that_ip
and so on...
The issue here with the for loop is that all the entries are with the same server ip (while I should have an entry for each of the servers with their respective hostnames):
[IC]
10.241.55.6 default_hostname=server1
10.241.55.6 default_hostname=server2
What I'm missing here? Also if there is any other better way to achieve this please let me know.
You're using the same variable twice in the template...
{{ ansible_default_ipv4.address }} default_hostname={{ ansible_nodename }}
{{ ansible_default_ipv4.address }} default_hostname={{ ansible_nodename }}
...so of course you're getting two identical lines. It sounds like you want to access the per-host value of this variable, which means you need to access it via hostvars.
Maybe something like this:
[IC]
{% for host in groups['IC'] %}
{{ hostvars[host].ansible_default_ipv4.address }} default_hostname={{ hostvars[host].ansible_nodename }}
{% endfor %}

How to use hosts in my playbook for initiating my template

I have a playbook to initiate a conf to servers that requires hostname. I want to pass my hosts that define in playbook as group so as to loop the variables in my jinja2 template. And I don't want to set the hosts in vars(because that was already define in playbook, I don't know the reason for re-define)
For example:
host file:
[test_servers]
t1
t2
t3
[test2_servers]
t2
t4
playbook:
- hosts: test_servers
tasks:
- name: generate my conf
template:
src: templates/temp.conf.j2
dest: "test.conf"
force: True
vars:
hosts: test_servers # So far I need to declare the var here duplicately, I've group my server in host file and I just want to use the current group.
temp.conf
....
{% for host in groups[hosts] %}
Entry.{{ loop.index }} = {{ host }}
{% endfor %}
....
I wonder if there is a better way to pass the hosts that set in playbook to my jinja2 template so I can re-use the playbook for different hosts. For example, I just need to reset the playbook hosts to test2, no need to rewrite the vars.
There is no special variable with the name of the group, but there is ansible_play_hosts_all
List of all the hosts that were targeted by the play
Remove vars and use ansible_play_hosts_all in the template
- hosts: test_servers
tasks:
- name: generate my conf
template:
src: templates/temp.conf.j2
dest: "test.conf"
force: True
{% for host in ansible_play_hosts_all %}
Entry.{{ loop.index }} = {{ host }}
{% endfor %}

Appending files with Template Module in Ansible

So I have an ansible playbook that is using a Jinja2 template to create a log file. Everytime I run the playbook it is pulling in customer information from customers.yml and outputting the completed template into a 'stunnel.conf' file. The template works fine but I am trying to find a way to append the previous 'stunnel.conf' rather than overwriting it using the Template module. I wish to add text to the beginning of the 'stunnel.conf' manually and not have it overwritten. Do you think this would be possible?
Stunnel.conf
; GFAM - PBSTP
[customer-GFAM-34074]
cert = /etc/stunnel/stunnel.pem
accept = 34094
connect = 35094
; GUANFABANK - FXSIM
[customer-GUANFABANK-34051]
cert = /etc/stunnel/stunnel.pem
accept = 34095
connect = 35095
; ONEZERO2 - TRADESTREAM
[customer-ONEZERO2-39124]
cert = /etc/stunnel/stunnel.pem
accept = 34096
connect = 35096
; BTG-VELOCITY - PBSTP
[customer-BTG-VELOCITY-42533]
cert = /etc/stunnel/stunnel.pem
accept = 34097
connect = 35097
Jinja2 Template
{#CONTEXT: {{ customers }}#}
{% set currentport = 34093%}
{% for cust, config in customers.items() %}
; {{ cust }} - {{ config['type'] }}
[customer-{{ cust }}-{{ config['accept'] }}]
cert = {{ "/etc/stunnel/stunnel.pem" }}
{#accept = {{ config['accept'] }}#}
{#connect = {{ config['connect'] }}#}
accept = {{ currentport + 1 }}
connect = {{ currentport + 1001 }}
{% set currentport = currentport + 1 %}
{% endfor %}
playbook.yml
- include_vars:
file: /home/vagrant/stunnelSimAnsPractice/roles/ns16/vars/customers.yml
name: customers
- template:
src: /home/vagrant/stunnelSimAnsPractice/roles/ns16/templates/stunnel.j2
dest: /home/vagrant/stunnelSimAnsPractice/roles/ns16/output/stunnel.conf
owner: root
group: root
You can use blockinfile module and template lookup to manage per-client blocks in your stunnel.conf:
- include_vars:
file: customers.yml
name: customers
- blockinfile:
dest: stunnel.conf
block: "{{ lookup('template', 'stunnel.j2') }}"
marker: "; {mark} ANSIBLE MANAGED BLOCK FOR {{ cust }}"
I've shortened file paths for readability.
This way Ansible will look for managed block for specific client ({{ cust }} variable) and add/replace with content from templated stunnel.j2.
I would like suggest to do it like this:
Save output of template to temporary file.
Append Stunnel.conf file with content of temporary file.
Delete temporary file.
In playbook it could look like:
- include_vars:
file: /home/vagrant/stunnelSimAnsPractice/roles/ns16/vars/customers.yml
name: customers
- template:
src: /home/vagrant/stunnelSimAnsPractice/roles/ns16/templates/stunnel.j2
dest: /home/vagrant/stunnelSimAnsPractice/roles/ns16/output/temp.conf
owner: root
group: root
- name: "Append stunnel.conf with content of temporary file"
shell: cat temp.conf >> stunnel.conf
args:
chdir: "/home/vagrant/stunnelSimAnsPractice/roles/ns16/output"
- name: "Delete temporary file"
file:
path: /home/vagrant/stunnelSimAnsPractice/roles/ns16/output/temp.conf
state: absent

Resources