Template, Roles
Managing configurations of multiple servers and environments is a big benefit of using Ansible. But what should we do when configuration files vary from server to server? Before creating a separate configuration for each server or environment, lets take a look at Ansible templates.
Sometimes we need to transfer text files to remote hosts. Those text files are usually configuration files. When we are working with a single server, the configuration file may contain information specific to that server like hostname, IP address, etc. Since we’re working with a single server, we could create the file on the Ansible controller and then use the copy module in a playbook to copy it to the server.
But what if we have multiple web servers each needing that same configuration file but each with their own specific values? We can’t just copy the configuration file to all machines; it’s only built for a single server with a specific hostname, IP address, etc. So we need an Ansible template.
Ansible templates allow you to define text files with variables instead of static values and then replace those variables at playbook runtime.
An Ansible template is a text file built with the Jinja2 templating language with a j2 file extension. A Jinja2 template looks exactly like the text file you’d like to get onto a remote host. The only difference is that instead of static values, the file contains variables.
For demonstration, lets install web server on both centos and ubuntu in our lab environment and make sure everything is going fine :
[[email protected] demo-temp]$ ansible ubuntu -b -m apt -a "name=apache2 state=present"
[[email protected] demo-temp]$ ansible centos -b -m yum -a "name=httpd state=present"
Next create a template for web server default page (index.html.j2):
<html>
<center>
<h1> This machine's hostname is {{ ansible_hostname }}</h1>
<h2> os family is {{ ansible_os_family }}</h2>
<small>file version is {{ file_version }}</small>
{# This is comment, it will not appear in final output #}
</center>
</html>
and related playpook to use this template:
---
#sample playbook using template for web server- template-playbook.yaml
- hosts: all
become: yes
vars:
file_version: 1.0
tasks:
- name: install default web page
template:
src: index.html.j2
dest: /var/www/html/index.html
mode: 0777
We can change file permissions with the template module.
Next we will put index.html on each server and check the results:
[[email protected] demo-temp]$ ansible-playbook template-playbook.yaml
PLAY [all] ******************************************************************************************************************************
TASK [Gathering Facts] ******************************************************************************************************************
ok: [centos]
ok: [ubuntu]
TASK [install default web page] *********************************************************************************************************
changed: [centos]
changed: [ubuntu]
PLAY RECAP ******************************************************************************************************************************
centos : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
ubuntu : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
[email protected]:~# cat /var/www/html/index.html
<html>
<center>
<h1> This machine's hostname is ubuntu</h1>
<h2> os family is Debian</h2>
<small>file version is 1.0</small>
</center>
</html>
[[email protected] ~]# cat /var/www/html/index.html
<html>
<center>
<h1> This machine's hostname is centos</h1>
<h2> os family is RedHat</h2>
<small>file version is 1.0</small>
</center>
</html>
To add multiple files on a remote host use loops to template multiple files.
How ansible templates work ?
All templating happens on the Ansible controller before the task is sent and executed on the target machine. This approach minimizes the package requirements on the target (jinja2 is only required on the controller). It also limits the amount of data Ansible passes to the target machine. Ansible parses templates on the controller and passes only the information needed for each task to the target machine, instead of passing all the data on the controller and parsing it on the target.
Using include statements is our trick to split a large playbook into smaller pieces. We can also move task to a separate file and use include statement to include tasks from:

Lets see an example: