Ansible – Ansible Templates (Part-9)

Posted by

We can declare variables in

  • playbook
  • inventory
  • commands

Which can be used & Intropolate only in playbook Tasks but not in the files used in playbook.

Solution: Template
Using Template module you can Interpolate vars mentioned in the files used in playbook

Rule for using template Module

  • A file must be .j2 ext
  • Must use a module call “template”

What is a Template in Ansible?

In Ansible, a template is a file that contains variables and expressions using the Jinja2 templating engine. Templates are used to generate dynamic content, which can be configuration files, scripts, or any other text files. The template files typically have a .j2 extension to denote that they are Jinja2 templates.

Importance of Templates

Templates are important in Ansible for several reasons:

  1. Dynamic Content Generation: Templates allow you to create files that can change based on the variables and facts available at runtime. This makes your configuration files flexible and adaptable to different environments.
  2. Reuse and Maintainability: Using templates reduces duplication by enabling you to reuse the same template file across multiple hosts or environments with different values.
  3. Consistency: Templates help ensure that the generated files are consistent across different deployments, reducing the risk of errors and configuration drift.

Why Use Templates?

Using templates in Ansible is beneficial because it allows you to:

  • Automate the creation of configuration files and scripts.
  • Dynamically adjust configurations based on the host or environment.
  • Simplify and streamline your automation scripts by using variables and loops within templates.

How to Use Templates in Ansible

Basic Usage

1.Create a Template FileCreate a Jinja2 template file with placeholders for variables. For example, nginx.conf.j2:

server {
    listen 80;
    server_name {{ server_name }};

    location / {
        proxy_pass http://{{ proxy_pass }};
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

2.Use the Template Module in a Playbook

Use the template module in your playbook to apply the template and generate the final configuration file.

- name: Configure Nginx
  hosts: webservers
  become: yes
  vars:
    server_name: example.com
    proxy_pass: backend.example.com
  tasks:
    - name: Deploy Nginx configuration
      ansible.builtin.template:
        src: nginx.conf.j2
        dest: /etc/nginx/sites-available/default
      notify:
        - Reload Nginx

    - name: Ensure Nginx is running
      ansible.builtin.service:
        name: nginx
        state: started

Advanced Usage with Loops and Conditionals

Templates can also include loops and conditionals to create more complex and dynamic content.

1.Template File with Loops and ConditionalsFor example, hosts.j2:

{% for host in groups['all'] %}
{{ host }} ansible_host={{ hostvars[host]['ansible_host'] }} ansible_user={{ hostvars[host]['ansible_user'] }}
{% endfor %}

2.Playbook Using the Template

- name: Generate /etc/hosts file
  hosts: localhost
  vars:
    ansible_host: "{{ inventory_hostname }}"
    ansible_user: "{{ ansible_user_id }}"
  tasks:
    - name: Create /etc/hosts
      ansible.builtin.template:
        src: hosts.j2
        dest: /etc/hosts

Using Facts in Templates

You can use Ansible facts to dynamically populate templates with information about the hosts.

1.Template File Using FactsFor example, motd.j2:

Welcome to {{ ansible_facts['hostname'] }}!
Your IP address is {{ ansible_facts['default_ipv4']['address'] }}.

2.Playbook Applying the Template

- name: Generate /etc/motd
  hosts: all
  tasks:
    - name: Create /etc/motd
      ansible.builtin.template:
        src: motd.j2
        dest: /etc/motd

Real-World Example: Configuring an Application

Imagine you are deploying an application that requires a configuration file with environment-specific settings. Using Ansible templates, you can automate the creation of these configuration files.

  1. Template File for Application Config (app_config.j2)
[application]
environment = {{ env }}
debug = {{ debug }}
database_url = {{ database_url }}

[logging]
level = {{ log_level }}

2. Playbook to Deploy Application Configuration

- name: Deploy application configuration
  hosts: app_servers
  become: yes
  vars:
    env: production
    debug: false
    database_url: postgres://user:password@db.example.com/dbname
    log_level: INFO
  tasks:
    - name: Create application configuration
      ansible.builtin.template:
        src: app_config.j2
        dest: /etc/myapp/config.ini
      notify:
        - Restart application

handlers:
  - name: Restart application
    ansible.builtin.service:
      name: myapp
      state: restarted

Templates in Ansible are powerful tools that enable dynamic content generation, flexibility, and maintainability in your automation scripts. By leveraging the Jinja2 templating engine, you can create complex configurations that adapt to different environments and host-specific settings. Understanding how to use templates effectively can greatly enhance your ability to manage configurations and deploy applications consistently and efficiently.

Example Code for Cento/RHEL


---
- name: Update web servers
  hosts: web
  vars:
    myname: "Rajesh Kumar"

  tasks:
  - name: Install Apache in centos7
    ansible.builtin.yum:
      name: httpd
      state: latest
  - name: Copy index.html
    ansible.builtin.copy:
      src: index.html
      dest: /var/www/html/index.html
  - name: Template index.html
    template:
      src: index.html.j2
      dest: /var/www/html/index-template.html
  - name: Starting a Apache Server
    ansible.builtin.service:
      name: httpd
      state: started

index.html.j2

Project Requirement

  • When you Change APACHE STATIC HTML Content -> HTTPD restart is not required
  • When you Change APACHE Conf -> HTTPD restart is not required
  • HTTP should be run 90
  • No restart should be done on STATIC
  • Restart must be done at conf changes

---
- name: Update web servers
  hosts: web
  vars:
    myname: "Rajesh Kumar"
    httpport: 8090

  tasks:
  - name: Install Apache in centos7
    ansible.builtin.yum:
      name: httpd
      state: latest
  - name: Copy index.html
    ansible.builtin.copy:
      src: index.html
      dest: /var/www/html/index.html
  - name: Starting a Apache Server
    ansible.builtin.service:
      name: httpd
      state: started
  - name: stopped a Apache Server
    ansible.builtin.service:
      name: httpd
      state: stopped
  - name: Template for httpd.conf
    template:
      src: httpd.conf.j2
      dest: /etc/httpd/conf/httpd.conf
  - name: Starting a Apache Server
    ansible.builtin.service:
      name: httpd
      state: started

Example code for Template for Ubuntu


---
- name: Update web servers
  hosts: web
  vars:
    myname: "Rajesh Kumar"
    port: 81

  tasks:
  - name: Install Apache in Ubuntu
    ansible.builtin.apt:
      name: apache2
      state: latest
  - name: Copy index.html
    ansible.builtin.copy:
      src: index.html
      dest: /var/www/html/index.html
  - name: stopped a Apache Server
    ansible.builtin.service:
      name: apache2
      state: stopped
  - name: Template for httpd.conf
    template:
      src: ports.conf.j2
      dest: /etc/apache2/ports.conf
  - name: Starting a Apache Server
    ansible.builtin.service:
      name: apache2
      state: started

Live Practical:

Create temp.yaml file

---
- name: Update web servers
  hosts: web
  vars:
    myname: "Rajesh Kumar"
    port: 81

  tasks:
  - name: Install Apache in Ubuntu
    ansible.builtin.apt:
      name: apache2
      state: latest
  - name: Copy index.html
    ansible.builtin.copy:
      src: /home/jami/index.html
      dest: /var/www/html/index.html
  - name: stopped a Apache Server
    ansible.builtin.service:
      name: apache2
      state: stopped
  - name: Template for httpd.conf
    template:
      src: /home/jami/ports.conf.j2
      dest: /etc/apache2/ports.conf
  - name: Starting a Apache Server
    ansible.builtin.service:
      name: apache2
      state: started

move ports.conf from /etc/apache2 to your source directly /home/jami/ports.conf in ACS change port.conf file to port.conf.j2 and change list to Listen {{port}} so it will listen to port 81 instead of 80

cd /etc/apache2
cp ports.conf /root/
cd /root/
vi ports.conf
mv ports.conf ports.conf.j2

vi /home/jami/ports.conf.j2

Listen {{port}}

<IfModule ssl_module>
        Listen 443
</IfModule>

<IfModule mod_gnutls.c>
        Listen 443
</IfModule>

# vim: syntax=apache ts=4 sw=4 sts=4 sr noet

Run the ansible playbook

ansible-playbook -i inventory temp.yaml -u jami -k -b

Now web server listen to 81 instead of 80

Problems

  • No change in config file but STOP and START happening

Solution

  • Handlers

Handler in Ansible

What are Handlers in Ansible?

Handlers in Ansible are special tasks that are triggered by other tasks using the notify directive. They are used to perform actions that should occur only if there have been changes to the system, such as restarting a service after a configuration file has been updated. Handlers help ensure that changes are applied only when necessary, making them crucial for efficient and effective configuration management.

Importance of Handlers

  1. Conditional Execution: Handlers run only when notified, ensuring actions are taken only when needed.
  2. Efficiency: Reduces unnecessary operations, such as restarting a service when there have been no changes.
  3. Consistency: Helps maintain a consistent state by ensuring dependent tasks are executed after changes.

How to use handler for above example

Handlers Code Example for Ubuntu

---
- name: Update web servers
  hosts: localhost
  vars:
    myname: "Rajesh Kumar"
    port: 81

  tasks:
  - name: Install Apache in Ubuntu
    ansible.builtin.apt:
      name: apache2
      state: latest
  - name: Copy index.html
    ansible.builtin.copy:
      src: index.html
      dest: /var/www/html/index.html
  - name: Template for httpd.conf
    template:
      src: ports.conf.j2
      dest: /etc/apache2/ports.conf
    notify:
      - ReStarting a Apache Server
  - name: Starting a Apache Server
    ansible.builtin.service:
      name: apache2
      state: started

  handlers:
  - name: ReStarting a Apache Server
    ansible.builtin.service:
      name: apache2
      state: restarted

No sopped of server if no changes made to file.

For centros

---
- name: Update web servers
  hosts: localhost
  vars:
    myname: "Rajesh Kumar"
    port: 81

  tasks:
  - name: Install Apache in Ubuntu
    ansible.builtin.apt:
      name: apache2
      state: latest
  - name: Copy index.html
    ansible.builtin.copy:
      src: index.html
      dest: /var/www/html/index.html
  - name: Template for httpd.conf
    template:
      src: ports.conf.j2
      dest: /etc/apache2/ports.conf
    notify:
      - ReStarting a Apache Server
  - name: Starting a Apache Server
    ansible.builtin.service:
      name: apache2
      state: started

  handlers:
  - name: ReStarting a Apache Server
    ansible.builtin.service:
      name: apache2
      state: restarted

Basic Usage

  1. Define a HandlerHandlers are defined in the same way as regular tasks but are listed under the handlers section.
handlers:
  - name: Restart Apache
    ansible.builtin.service:
      name: apache2
      state: restarted

2.Notify a Handler

Use the notify keyword in a task to trigger the handler when the task results in a change.

tasks:
  - name: Copy new Apache config
    ansible.builtin.copy:
      src: /path/to/httpd.conf
      dest: /etc/httpd/conf/httpd.conf
    notify:
      - Restart Apache

3. Complete Playbook Example

Here’s a complete playbook that demonstrates the use of handlers:

---
- name: Ensure Apache is installed and running
  hosts: webservers
  become: yes
  tasks:
    - name: Install Apache
      ansible.builtin.yum:
        name: httpd
        state: present

    - name: Copy Apache config
      ansible.builtin.copy:
        src: /path/to/httpd.conf
        dest: /etc/httpd/conf/httpd.conf
      notify:
        - Restart Apache

  handlers:
    - name: Restart Apache
      ansible.builtin.service:
        name: httpd
        state: restarted

Advanced Usage

  1. Multiple HandlersYou can define and notify multiple handlers. Handlers will run in the order they are defined.
tasks:
  - name: Copy Apache config
    ansible.builtin.copy:
      src: /path/to/httpd.conf
      dest: /etc/httpd/conf/httpd.conf
    notify:
      - Restart Apache
      - Send Notification

handlers:
  - name: Restart Apache
    ansible.builtin.service:
      name: httpd
      state: restarted

  - name: Send Notification
    ansible.builtin.command: /usr/local/bin/send_notification

2.Handlers in Different Roles

Handlers can be defined in different roles and notified across roles.

Role 1: Webserver Role

- name: Webserver Configuration
  hosts: webservers
  roles:
    - role: webserver

Role 2: Database Role

- name: Database Configuration
  hosts: dbservers
  roles:
    - role: database

Webserver Role Definition (roles/webserver/tasks/main.yml)

- name: Install Apache
  ansible.builtin.yum:
    name: httpd
    state: present

- name: Copy Apache config
  ansible.builtin.copy:
    src: /path/to/httpd.conf
    dest: /etc/httpd/conf/httpd.conf
  notify:
    - Restart Apache

handlers:
  - name: Restart Apache
    ansible.builtin.service:
      name: httpd
      state: restarted

3. Running Handlers Only Once

If multiple tasks notify the same handler, the handler will run only once, at the end of the play.

tasks:
  - name: Task 1
    ansible.builtin.copy:
      src: /path/to/file1
      dest: /destination/file1
    notify:
      - Handler

  - name: Task 2
    ansible.builtin.copy:
      src: /path/to/file2
      dest: /destination/file2
    notify:
      - Handler

handlers:
  - name: Handler
    ansible.builtin.command: /usr/local/bin/handler_script

3.

Using meta: flush_handlers

By default, handlers run at the end of the play. You can force handlers to run at a specific point in your playbook using meta: flush_handlers.

tasks:
  - name: Task 1
    ansible.builtin.copy:
      src: /path/to/file1
      dest: /destination/file1
    notify:
      - Handler

  - name: Flush Handlers
    meta: flush_handlers

  - name: Task 2
    ansible.builtin.copy:
      src: /path/to/file2
      dest: /destination/file2

Example: Real-World Use Case

Scenario: You have a web server where you need to update the application configuration and restart the application service only if the configuration changes.

  1. Playbook Definition
---
- name: Deploy application configuration
  hosts: app_servers
  become: yes
  tasks:
    - name: Copy application config
      ansible.builtin.template:
        src: app_config.j2
        dest: /etc/myapp/config.ini
      notify:
        - Restart MyApp

  handlers:
    - name: Restart MyApp
      ansible.builtin.service:
        name: myapp
        state: restarted

2. Template File (app_config.j2)

[application]
environment = {{ env }}
debug = {{ debug }}
database_url = {{ database_url }}

3. Running the PlaybookWhen you run the playbook, the handler Restart MyApp will be triggered only if the configuration file changes, ensuring the application is restarted only when necessary.

guest
0 Comments
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x