Template, Roles

Ansible Template

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.

What is an Ansible Template?

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.

Ansible Template files

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 :
1
[[email protected] demo-temp]$ ansible ubuntu -b -m apt -a "name=apache2 state=present"
2
[[email protected] demo-temp]$ ansible centos -b -m yum -a "name=httpd state=present"
Copied!
Next create a template for web server default page (index.html.j2):
1
<html>
2
<center>
3
<h1> This machine's hostname is {{ ansible_hostname }}</h1>
4
<h2> os family is {{ ansible_os_family }}</h2>
5
<small>file version is {{ file_version }}</small>
6
{# This is comment, it will not appear in final output #}
7
</center>
8
</html>
Copied!
and related playpook to use this template:
1
---
2
#sample playbook using template for web server- template-playbook.yaml
3
4
- hosts: all
5
become: yes
6
vars:
7
file_version: 1.0
8
9
tasks:
10
- name: install default web page
11
template:
12
src: index.html.j2
13
dest: /var/www/html/index.html
14
mode: 0777
Copied!
We can change file permissions with the template module.
Next we will put index.html on each server and check the results:
1
[[email protected] demo-temp]$ ansible-playbook template-playbook.yaml
2
3
PLAY [all] ******************************************************************************************************************************
4
5
TASK [Gathering Facts] ******************************************************************************************************************
6
ok: [centos]
7
ok: [ubuntu]
8
9
TASK [install default web page] *********************************************************************************************************
10
changed: [centos]
11
changed: [ubuntu]
12
13
PLAY RECAP ******************************************************************************************************************************
14
centos : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
15
ubuntu : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Copied!
1
[email protected]:~# cat /var/www/html/index.html
2
<html>
3
<center>
4
<h1> This machine's hostname is ubuntu</h1>
5
<h2> os family is Debian</h2>
6
<small>file version is 1.0</small>
7
</center>
8
</html>
Copied!
1
[[email protected] ~]# cat /var/www/html/index.html
2
<html>
3
<center>
4
<h1> This machine's hostname is centos</h1>
5
<h2> os family is RedHat</h2>
6
<small>file version is 1.0</small>
7
</center>
8
</html>
Copied!
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.

include statement

include plays and tasks

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:
1
[[email protected] demo-file]$ cat update-systems-play.yaml
2
---
3
4
- hosts: all
5
become: yes
6
7
tasks:
8
- name: update apt
9
apt: upgrade=dist update_cache=yes
10
when: ansible_os_family == "Debian"
11
12
- name: update yum
13
yum: name=* state=latest update_cache=yes
14
when: ansible_os_family == "RedHat"
Copied!
1
[[email protected] demo-file]$ cat install-web-task.yaml
2
---
3
4
- name: install on debian
5
apt: name=apache2 state=latest update_cache=yes
6
when: ansible_os_family == "Debian"
7
8
- name: install on centos
9
yum: name=httpd state=latest update_cache=yes
10
when: ansible_os_family == "RedHat"
11
12
- name: start debian service
13
service: name=apache2 enabled=yes state=started
14
when: ansible_os_family == "Debian"
15
16
- name: start centos service
17
service: name=httpd enabled=yes state=started
18
when: ansible_os_family == "RedHat"
19
Copied!
and finally or main playbook:
1
[[email protected] demo-file]$ cat include-playbook.yaml
2
---
3
4
- include: update-systems-play.yaml
5
6
- hosts: all
7
become: yes
8
tasks:
9
- include: install-web-task.yaml
Copied!
and lets check the results:
1
[[email protected] demo-file]$ ansible-playbook include-playbook.yaml
2
3
PLAY [all] *********************************************************************
4
5
TASK [Gathering Facts] *********************************************************
6
ok: [centos]
7
ok: [ubuntu]
8
9
TASK [update apt] **************************************************************
10
skipping: [centos]
11
changed: [ubuntu]
12
13
TASK [update yum] **************************************************************
14
skipping: [ubuntu]
15
changed: [centos]
16
17
PLAY [all] *********************************************************************
18
19
TASK [Gathering Facts] *********************************************************
20
ok: [centos]
21
ok: [ubuntu]
22
23
TASK [install on debian] *******************************************************
24
skipping: [centos]
25
ok: [ubuntu]
26
27
TASK [install on centos] *******************************************************
28
skipping: [ubuntu]
29
ok: [centos]
30
31
TASK [start debian service] ****************************************************
32
skipping: [centos]
33
ok: [ubuntu]
34
35
TASK [start centos service] ****************************************************
36
skipping: [ubuntu]
37
changed: [centos]
38
39
PLAY RECAP *********************************************************************
40
centos : ok=5 changed=2 unreachable=0 failed=0 skipped=3 rescued=0 ignored=0
41
ubuntu : ok=5 changed=1 unreachable=0 failed=0 skipped=3 rescued=0 ignored=0
Copied!

Ansible Roles

Like any other programming or scripting language we can start writing huge play books with hundreds of lines, but it is against the simplicity. Also writing long playbooks make them useful just for a specific use case and no other one can use it!
In programming languages, we try to modularize our code into packages, modules, classes and functions. That way our code becomes more organized, readable and we can share it with others.
Obviously we don't have any programs or classes or functions in Ansible, what we do have are just inventory files, variables and playbooks. What should we do? When most system administrators use ansible they use roles as the way to organize various plays and playbooks.
Previously we learned how to include files, it could be confusing, what kind of file are you including? is it a task file? is it a playfile? How it's included? where it's included? The idea of roles give us a folder structure to storing those files and it also automatically include files that are named and placed in a proper place.
Roles is a new way to organize things. This is very common that people share roles. Usually the file extention on roles is going to be .yml on the YAML files instead of .yaml . That is because the lent of extention is not important in linux, but when you are sharing roles with other people it is important.
So roles provide a mechanism to break a complicated playbook into multiple reusable components. Each component offers a small function that can be used independently within the playbook. So rather than creating one complex playbook, you can create many roles and simply drop them into your playbooks.
Lets take a look at an example:
1
[[email protected] demo-roles]$ tree -F
2
.
3
├── roles/
4
│   └── apache/
5
│   ├── handlers/
6
│   │   └── main-handlers.yml
7
│   └── tasks/
8
│   └── main-tasks.yml
9
└── site-playbook.yml
10
11
4 directories, 3 files
Copied!
1
[[email protected] demo-roles]$ cat roles/apache/tasks/main-tasks.yml
2
---
3
4
5
- name: install on debian
6
apt: name=apache2 state=latest update_cache=yes
7
when: ansible_os_family == "Debian"
8
notify: start debian service
9
10
- name: install on centos
11
yum: name=httpd state=latest update_cache=yes
12
when: ansible_os_family == "RedHat"
13
notify: start centos service
Copied!
1
[[email protected] demo-roles]$ cat roles/apache/handlers/main-handlers.yml
2
---
3
4
- name: start debian service
5
service: name=apache2 enabled=yes state=started
6
7
- name: start centos service
8
service: name=httpd enabled=yes state=started
Copied!
and finally our playbook:
1
[[email protected] demo-roles]$ cat site-playbook.yml
2
---
3
4
- hosts: all
5
become: yes
6
roles:
7
- apache
Copied!
Lets run it:
1
[[email protected] demo-roles]$ ansible-playbook site-playbook.yml
2
3
PLAY [all] *************************************************************************************************************************************
4
5
TASK [Gathering Facts] *************************************************************************************************************************
6
ok: [centos]
7
ok: [ubuntu]
8
9
PLAY RECAP *************************************************************************************************************************************
10
centos : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
11
ubuntu : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Copied!
when we want to create a role, we can either create required file structure manually or we can use ansible-galaxy command:
1
[[email protected] demo-roles]$ ansible-galaxy init new_role
2
- Role new_role was created successfully
Copied!
1
.
2
├── new_role
3
│   ├── defaults
4
│   │   └── main.yml
5
│   ├── files
6
│   ├── handlers
7
│   │   └── main.yml
8
│   ├── meta
9
│   │   └── main.yml
10
│   ├── README.md
11
│   ├── tasks
12
│   │   └── main.yml
13
│   ├── templates
14
│   ├── tests
15
│   │   ├── inventory
16
│   │   └── test.yml
17
│   └── vars
18
│   └── main.yml
19
|
20
.
21
.
22
.
Copied!
You can remove unnecessary files, and do not forget to edit README file!
that's all.
.
.
.
.
.
Last modified 3mo ago