DevOps Day 92: Managing Jinja2 Templates Using Ansible¶
This document outlines the solution for DevOps Day 92. The objective was to enhance an existing Ansible role for httpd by adding a dynamic Jinja2 template for the index.html file. This demonstrates how Ansible can customize configuration files based on the specific server it is deploying to.
Table of Contents¶
- DevOps Day 92: Managing Jinja2 Templates Using Ansible
- Table of Contents
- Task Overview
- Step-by-Step Solution
- Deep Dive: Ansible Concepts Used
- Internal Execution Flow
Task Overview¶
Objective: Deploy an httpd role to App Server 1 (stapp01) that includes a dynamically generated index.html file using a Jinja2 template.
Requirements:
1. Playbook: Update ~/ansible/playbook.yml to target stapp01 and use the httpd role.
2. Template: Create index.html.j2 inside the role's templates directory. It must use the {{ inventory_hostname }} variable.
3. Task: Add a task to main.yml to deploy this template to /var/www/html/index.html.
4. Permissions: Set file permissions to 0777 and ownership to the respective user (e.g., tony for stapp01).
Step-by-Step Solution¶
1. Create the Template¶
The core requirement is to create a file that changes its content based on where it is deployed.
Command:
cd ~/ansible/role/httpd/templates/
vi index.html.j2
Content:
This file was created using Ansible on {{ inventory_hostname }}
.j2 Extension: This signifies a Jinja2 template file. Ansible processes this file before sending it to the remote server.
* {{ inventory_hostname }}: This is an Ansible "magic variable". When the playbook runs on stapp01, Ansible automatically replaces this placeholder with the string "stapp01".
2. Update the Role Tasks¶
Next, I updated the role's main task file to include the template deployment step.
Command:
cd ~/ansible/role/httpd/tasks/
vi main.yml
Content:
---
# tasks file for role/httpd
- name: install the latest version of HTTPD
yum:
name: httpd
state: latest
- name: Start service httpd
service:
name: httpd
state: started
- name: Copy index.html template
template:
src: index.html.j2
dest: /var/www/html/index.html
mode: '0777'
owner: "{{ ansible_user }}"
group: "{{ ansible_user }}"
template module: This module reads the local .j2 file, processes the variables inside it, and writes the resulting static file to the remote dest.
* owner: "{{ ansible_user }}": This ensures the file is owned by the user we connected as (e.g., tony), fulfilling the dynamic ownership requirement.
3. Configure the Playbook¶
I updated the main playbook to call the role for the correct host.
Command:
cd ~/ansible/
vi playbook.yml
Content:
---
- hosts: stapp01
become: yes
become_user: root
roles:
- role/httpd
4. Execute and Validate¶
I ran the playbook and verified the result.
Execution Command:
ansible-playbook -i inventory playbook.yml
Verification:
ansible -i inventory stapp01 -a "cat /var/www/html/index.html"
This file was created using Ansible on stapp01
Deep Dive: Ansible Concepts Used¶
Jinja2 Templates¶
Jinja2 is a modern and designer-friendly templating language for Python. In Ansible, it allows you to:
* Dynamic Content: Insert variable values (like IPs, hostnames, usernames).
* Logic: Use {% if %} statements or {% for %} loops to generate complex configuration files (e.g., adding a config block only if a certain variable is true).
The template Module¶
Unlike the copy module, which transfers a file exactly as-is, the template module processes the file on the Ansible control node first.
1. Ansible reads src (local).
2. The Jinja2 engine replaces all {{ variables }} with their actual values for the current host.
3. The rendered file is transferred to dest (remote).
Ansible Roles¶
Roles are the primary way to break a playbook into multiple files. This simplifies writing complex playbooks and makes them easier to reuse. A role structure looks like:
* tasks/main.yml: The main list of tasks to execute.
* templates/: Where .j2 files are stored.
* handlers/: Handlers like "restart service".
* vars/: Variables specific to the role.
Internal Execution Flow¶
When you ran ansible-playbook -i inventory playbook.yml, the following process occurred internally:
- Parsing: Ansible read
playbook.yml, identified the target hoststapp01, and saw it needed to run therole/httpd. - Inventory Lookup: It looked up
stapp01in theinventoryfile to find the IP address, SSH user (tony), and password (Ir0nM@n). - Fact Gathering: It connected to
stapp01via SSH and ran thesetupmodule to gather facts (IP addresses, OS version, hostname). This populated theinventory_hostnamevariable. - Task Execution (Yum/Service): It executed the
yumandservicetasks usingsudoprivileges (become: yes). - Templating Engine:
- Ansible paused at the
templatetask. - On the Jump Host (Local), it loaded
index.html.j2. - It found
{{ inventory_hostname }}and replaced it with the value"stapp01". - It found
{{ ansible_user }}and replaced it with"tony".
- Ansible paused at the
- File Transfer: Ansible securely transferred the rendered content (now just plain text) to a temporary file on
stapp01. - Finalize: It moved the temporary file to
/var/www/html/index.htmland set the permissions to0777and owner totony.