Ansible Jinja2 Templates

AnsibleAnsibleBeginner
Practice Now

Introduction

In this lab, you will explore Jinja2, the powerful templating engine used by Ansible. Jinja2 allows you to create dynamic content, making your Ansible playbooks more flexible and adaptable. By the end of this lab, you'll understand how to use Jinja2 syntax in Ansible, including variables, filters, conditionals, and loops. This knowledge will enable you to create more dynamic and efficient Ansible playbooks and roles.


Skills Graph

%%%%{init: {'theme':'neutral'}}%%%% flowchart RL ansible(("`Ansible`")) -.-> ansible/ModuleOperationsGroup(["`Module Operations`"]) ansible(("`Ansible`")) -.-> ansible/PlaybookEssentialsGroup(["`Playbook Essentials`"]) ansible/ModuleOperationsGroup -.-> ansible/template("`Generate Files from Templates`") ansible/PlaybookEssentialsGroup -.-> ansible/playbook("`Execute Playbook`") ansible/PlaybookEssentialsGroup -.-> ansible/loop("`Iteration`") subgraph Lab Skills ansible/template -.-> lab-390470{{"`Ansible Jinja2 Templates`"}} ansible/playbook -.-> lab-390470{{"`Ansible Jinja2 Templates`"}} ansible/loop -.-> lab-390470{{"`Ansible Jinja2 Templates`"}} end

Understanding Jinja2 Basics

Jinja2 is a modern and designer-friendly templating language for Python, modeled after Django's templates. In Ansible, Jinja2 is used to template files, create dynamic play structures, and more.

Let's start by creating a simple Jinja2 template. First, navigate to your project directory:

cd ~/project

Now, let's create a directory for our templates:

mkdir templates
cd templates

Create a new file named hello.j2:

nano hello.j2

Add the following content:

Hello, {{ name }}!

Today is {{ ansible_date_time.date }}.
Your home directory is {{ ansible_env.HOME }}.

Save and exit the nano editor (Ctrl+X, then Y, then Enter).

In this template:

  • {{ name }} is a variable that will be replaced with an actual value when the template is rendered.
  • {{ ansible_date_time.date }} and {{ ansible_env.HOME }} are Ansible facts that will be automatically populated.

Now, let's create a playbook to use this template. Go back to the project root and create a new file named use_template.yml:

cd ~/project
nano use_template.yml

Add the following content:

---
- name: Use Jinja2 template
  hosts: localhost
  vars:
    name: "Ansible Learner"
  tasks:
    - name: Create file from template
      template:
        src: templates/hello.j2
        dest: /tmp/hello.txt

This playbook uses the template module to render our Jinja2 template and create a file at /tmp/hello.txt.

Run the playbook:

ansible-playbook use_template.yml

After running the playbook, check the contents of the created file:

cat /tmp/hello.txt

You should see the rendered template with the variable and facts replaced with actual values.

Using Jinja2 Filters

Jinja2 filters are functions that can be applied to variables. They are separated from the variable by a pipe symbol (|). Ansible includes many built-in filters, and you can also create custom ones.

Let's create a new template that demonstrates some common filters. Create a new file named filters.j2:

cd ~/project/templates
nano filters.j2

Add the following content:

1. Uppercase filter: {{ name | upper }}
2. Length filter: {{ name | length }}
3. Default filter: {{ optional_var | default('No value provided') }}
4. Join filter: {{ fruits | join(', ') }}
5. Random item: {{ fruits | random }}
6. First item: {{ fruits | first }}
7. JSON encode: {{ complex_data | to_json }}

Now, let's create a playbook to use this template. Go back to the project root and create a new file named use_filters.yml:

cd ~/project
nano use_filters.yml

Add the following content:

---
- name: Use Jinja2 filters
  hosts: localhost
  vars:
    name: "ansible learner"
    fruits:
      - apple
      - banana
      - cherry
    complex_data:
      key1: value1
      key2:
        - item1
        - item2
  tasks:
    - name: Create file from template
      template:
        src: templates/filters.j2
        dest: /tmp/filters.txt

    - name: Display file contents
      command: cat /tmp/filters.txt
      register: file_contents

    - name: Show file contents
      debug:
        var: file_contents.stdout_lines

This playbook applies various Jinja2 filters to our variables and displays the results.

Run the playbook:

ansible-playbook use_filters.yml

The output will show you how each filter modifies the input data. This demonstrates how filters can be used to manipulate data within your templates.

Jinja2 Conditionals

Jinja2 allows you to use conditional statements in your templates. This is particularly useful when you need to include or exclude content based on certain conditions.

Create a new template file named conditionals.j2:

cd ~/project/templates
nano conditionals.j2

Add the following content:

{% if user_type == 'admin' %}
Welcome, Administrator!
You have full access to the system.
{% elif user_type == 'user' %}
Welcome, User!
You have limited access to the system.
{% else %}
Welcome, Guest!
You have read-only access to the system.
{% endif %}

{% if fruits %}
Available fruits:
{% for fruit in fruits %}
  - {{ fruit }}
{% endfor %}
{% else %}
No fruits available.
{% endif %}

Now, let's create a playbook to use this template. Go back to the project root and create a new file named use_conditionals.yml:

cd ~/project
nano use_conditionals.yml

Add the following content:

---
- name: Use Jinja2 conditionals
  hosts: localhost
  vars:
    user_type: "admin"
    fruits:
      - apple
      - banana
      - cherry
  tasks:
    - name: Create file from template (admin)
      template:
        src: templates/conditionals.j2
        dest: /tmp/conditionals_admin.txt

    - name: Create file from template (user)
      template:
        src: templates/conditionals.j2
        dest: /tmp/conditionals_user.txt
      vars:
        user_type: "user"

    - name: Create file from template (guest)
      template:
        src: templates/conditionals.j2
        dest: /tmp/conditionals_guest.txt
      vars:
        user_type: "guest"
        fruits: []

    - name: Display file contents
      command: "cat {{ item }}"
      loop:
        - /tmp/conditionals_admin.txt
        - /tmp/conditionals_user.txt
        - /tmp/conditionals_guest.txt
      register: file_contents

    - name: Show file contents
      debug:
        var: file_contents.results[item].stdout_lines
      loop: [0, 1, 2]

This playbook creates three different files using the same template but with different variable values, demonstrating how the conditional statements in the template work.

Run the playbook:

ansible-playbook use_conditionals.yml

The output will show you how the template content changes based on the different conditions.

Jinja2 Loops

Jinja2 provides looping constructs that allow you to iterate over lists and dictionaries. This is particularly useful when you need to generate repetitive content based on variable data.

Create a new template file named loops.j2:

cd ~/project/templates
nano loops.j2

Add the following content:

1. Simple loop:
{% for item in simple_list %}
   - {{ item }}
{% endfor %}

2. Loop with index:
{% for item in simple_list %}
   {{ loop.index }}. {{ item }}
{% endfor %}

3. Loop over a dictionary:
{% for key, value in simple_dict.items() %}
   {{ key }}: {{ value }}
{% endfor %}

4. Nested loop:
{% for category, items in nested_dict.items() %}
   {{ category }}:
   {% for item in items %}
     - {{ item }}
   {% endfor %}
{% endfor %}

5. Conditional in loop:
{% for item in simple_list %}
   {% if loop.first %}First item: {% endif %}
   {% if loop.last %}Last item: {% endif %}
   {{ item }}
{% endfor %}

Now, let's create a playbook to use this template. Go back to the project root and create a new file named use_loops.yml:

cd ~/project
nano use_loops.yml

Add the following content:

---
- name: Use Jinja2 loops
  hosts: localhost
  vars:
    simple_list:
      - apple
      - banana
      - cherry
    simple_dict:
      name: John Doe
      age: 30
      city: New York
    nested_dict:
      fruits:
        - apple
        - banana
      vegetables:
        - carrot
        - potato
  tasks:
    - name: Create file from template
      template:
        src: templates/loops.j2
        dest: /tmp/loops.txt

    - name: Display file contents
      command: cat /tmp/loops.txt
      register: file_contents

    - name: Show file contents
      debug:
        var: file_contents.stdout_lines

This playbook demonstrates various ways to use loops in Jinja2 templates.

Run the playbook:

ansible-playbook use_loops.yml

The output will show you how the different loop constructs in the template generate content based on the provided variables.

Advanced Jinja2 Techniques

In this final step, we'll explore some advanced Jinja2 techniques, including macros and filters. Macros are reusable template snippets, similar to functions in programming languages.

Create a new template file named advanced.j2:

cd ~/project/templates
nano advanced.j2

Add the following content:

{% macro render_list(title, items) %}
{{ title }}:
{% for item in items %}
  - {{ item }}
{% endfor %}
{% endmacro %}

1. Using a macro:
{{ render_list("Fruits", fruits) }}

{{ render_list("Vegetables", vegetables) }}

2. Using set:
{% set greeting = "Hello, " + name + "!" %}
{{ greeting }}

3. Custom filter (defined in playbook):
{{ complex_data | to_nice_json }}

4. Whitespace control:
{% for item in fruits -%}
    {{ item }}
{%- if not loop.last %}, {% endif %}
{%- endfor %}

5. Raw block (no processing):
{% raw %}
This {{ will_not_be_processed }}
{% endraw %}

6. Include another template:
{% include 'simple_include.j2' %}

Now, let's create the simple include file. Create a new file named simple_include.j2:

nano simple_include.j2

Add the following content:

This is content from an included template.
Current user: {{ ansible_user_id }}

Now, let's create a playbook to use this advanced template. Go back to the project root and create a new file named use_advanced.yml:

cd ~/project
nano use_advanced.yml

Add the following content:

---
- name: Use advanced Jinja2 techniques
  hosts: localhost
  vars:
    name: "Ansible Expert"
    fruits:
      - apple
      - banana
      - cherry
    vegetables:
      - carrot
      - potato
      - broccoli
    complex_data:
      key1: value1
      key2:
        - item1
        - item2
    custom_filters:
      to_nice_json: "{{ '(' ~ ((complex_data | to_json) | replace('\"', '\\\"')) ~ ')' | from_json | to_nice_json }}"
  tasks:
    - name: Create file from template
      template:
        src: templates/advanced.j2
        dest: /tmp/advanced.txt

    - name: Display file contents
      command: cat /tmp/advanced.txt
      register: file_contents

    - name: Show file contents
      debug:
        var: file_contents.stdout_lines

This playbook demonstrates advanced Jinja2 techniques, including macros, custom filters, whitespace control, and including other templates.

Run the playbook:

ansible-playbook use_advanced.yml

The output will show you how these advanced Jinja2 techniques work in practice.

Summary

In this lab, you've explored Jinja2 templating in Ansible, covering a wide range of features and techniques. Here are the key takeaways:

  1. Basic Jinja2 syntax: You learned how to use variables and Ansible facts in templates.
  2. Jinja2 filters: You explored various built-in filters to manipulate data within templates.
  3. Conditionals: You used if-elif-else statements to create dynamic content based on conditions.
  4. Loops: You learned how to iterate over lists and dictionaries, including nested loops and loop controls.
  5. Advanced techniques: You explored macros, custom filters, whitespace control, raw blocks, and template inclusion.

These Jinja2 features allow you to create highly dynamic and flexible templates in Ansible. They enable you to generate configuration files, scripts, and other content that can adapt to different scenarios and data inputs.

As you continue working with Ansible, you'll find that mastering Jinja2 templating is crucial for creating efficient and adaptable playbooks and roles. It allows you to write more concise and maintainable code, reducing repetition and making your Ansible projects more scalable.

Remember that while Jinja2 is powerful, it's important to balance flexibility with readability. Overly complex templates can be difficult to maintain, so always strive for clarity in your template design.

Practice using these Jinja2 features in your Ansible projects, and you'll be able to handle increasingly complex automation tasks with ease.

Other Ansible Tutorials you may like