Going to AWS re:Invent 2024?

➡️ Book a meeting with Spacelift

Ansible

How to Create Ansible Template [Examples]

How To Create and Use Templates in Ansible

Spacelift and Ansible

Manage and orchestrate Ansible with Spacelift’s vibrant ecosystem and excellent GitOps flow. Create powerful custom workflows combining multiple IaC tools.

Book a demo

In this blog post, we will explore templating with Ansible to parametrize configuration files and leverage variables in templates.

What are Ansible templates? Templates are useful in cases where we would like to reuse a file but with different parameters for various use cases or environments.

If you are new to Ansible or interested in other Ansible concepts, these Ansible tutorials on Spacelift’s blog might be handy.

Intro to Ansible Templating

With Ansible templating, users can dynamically generate text-based files using templates, variables, and facts for configuration and other purposes. The main objective of using templates is to facilitate and automate the management of configuration files for different targets and requirements.

Imagine that you need to maintain multiple similar environments but with different requirements or specifications. Instead of manually creating, maintaining, and editing configuration files for each target system, we can leverage Ansible templates. We can then combine the templates with other Ansible concepts, such as facts and variables, to generate files tailored to each system’s specific needs without code duplication.

Updating configuration files becomes more manageable with this approach since we only have to perform the changes in one place and handle any inputs with variables that will be replaced with actual values during the playbook execution.

Ansible uses Jinja2 as the default templating engine to create dynamic content.

Templating with Jinja2

Jinja2 is a full-featured template engine for Python. The Jinja2 templating engine is quite powerful and widely used with other frameworks and applications such as Flask and Django. 

Jinja2 templates combine plain text files and special syntax to define and substitute dynamic content, embed variables, expressions, loops, and even conditional statements to generate complex output. According to the documentation, expressions are enclosed in double curly braces {{ }}, statements in curly braces with percent signs {% %}, and comments in {# #}.

Let’s have a look at some examples below:

  • Jinja2 example with a variable named favourite_color
My favourite color is {{ favourite_color }}
  • Jinja2 if statement example
{% if age > 18 %} 
You are an adult, and you can vote in the voting center: {{ voting_center }}
 {% else %}
Sorry, you are minor, and you can’t vote yet.
{% endif %}
  • Jinja2 loop example
Here’s a list of fruits:
{% for fruit in fruits %}
{{ fruit }}
{% endfor %} 

Something worth mentioning is that the templating happens before the task is sent to the target machine. Therefore this approach doesn’t require the installation of any extra packages on the target machine and minimizes the amount of data sent. 

Another helpful functionality is utilizing the standard filters and tests included in Jinja2 to perform different operations. Ansible also implements extensions to Jinja2, including extra filters for selecting and transforming data and Lookup plugins for retrieving data from external sources.

Demo: Create and Use a Template in an Ansible Playbook

To use templates in Ansible playbooks, we can use the template module, which takes as inputs the template and the target file, and other necessary parameters to customize the final output file. 

In the first example, we will create a template file test.conf.j2 with the contents of the example we saw earlier.

test.conf.j2

My favourite color is {{ favourite_color }}

{% if age > 18 %}
You are an adult, and you can vote in the voting center: {{ voting_center }}
{% else %}
Sorry, you are minor and you can’t vote yet.
{% endif %}

A list of fruits:
{% for fruit in fruits %}
 - {{ fruit }}
{% endfor %}

Now let’s create a simple playbook and use Ansible’s template module. Our playbook contains some values for the variables and only one templating task. 

test_templates_playbook.yml

- name: Playbook to test templates
 hosts: all
 vars:
   favourite_color: blue
   age: 21
   voting_center: ab456-g
   fruits:
     - banana
     - apple
     - mango
     - pear

 tasks:
 - name: Template test
   template:
     src: templates/test.conf.j2
     dest: /tmp/test.conf

If we execute this playbook the /tmp/test.conf file will be created based on the template and the inputs. Let’s check its contents after we have executed the above playbook.

/tmp/test.conf

My favourite color is blue

You are an adult, and you can vote in the voting center: ab456-g

A list of fruits:
  - banana
  - apple
  - mango
  - pear

We have used variables, if statements, and for loops to produce a final configuration file based on our input and the base template.

Finally, let’s see an example with a real use case. In the second example, we will use a template to create a configuration file for an Nginx web server.

Here’s the template that we will use.

nginx.conf.j2

server {
       listen {{ web_server_port }};
       listen [::]:{{ web_server_port }};
       root {{ nginx_custom_directory }};
       index index.html;
       location / {
               try_files $uri $uri/ =404;
       }
}

We will use this template in the playbook below to provision an Nginx web server.

main_playbook.yml

- name: Provision nginx web server
  hosts: all
  gather_facts: yes
  become: yes
  vars:
    nginx_version: 1.18.0-0ubuntu1.4
    nginx_custom_directory: /home/ubuntu/nginx
    web_server_port: 80
  tasks:
  - name: Update and upgrade apt
    ansible.builtin.apt:
      update_cache: yes
      cache_valid_time: 3600
      upgrade: yes


  - name: "Install Nginx to version {{ nginx_version }}"
    ansible.builtin.apt:
      name: "nginx={{ nginx_version }}"
      state: present


  - name: Copy the Nginx configuration file to the host
    template:
      src: templates/nginx.conf.j2
      dest: /etc/nginx/sites-available/default
  
  - name: Create link to the new config to enable it
    file:
      dest: /etc/nginx/sites-enabled/default
      src: /etc/nginx/sites-available/default
      state: link


  - name: Create Nginx directory
    ansible.builtin.file:
      path: "{{ nginx_custom_directory }}"
      state: directory


  - name: Copy index.html to the Nginx directory
    copy:
      src: files/index.html
      dest: "{{ nginx_custom_directory }}/index.html"
  - name: Restart the Nginx service
    service:
      name: nginx
      state: restarted

We also used a simple custom index.html file for the homepage of our web server.

index.html

<html>
  <head>
    <title> Hello from Nginx </title>
  </head>
  <body>
  <h1> This is our test webserver</h1>
  <p>This nginx web server was deployed by Ansible.</p>
  </body>
</html>

Let’s go ahead and run this playbook. For this demo, we have created a virtual machine locally with Vagrant to serve as Ansible’s target.

ansible tempates main playbook

Last step, let’s ssh into the local host, verify that everything has run successfully, and check the file /etc/nginx/sites-available/default generated from the template.

vagrant ansible templates

The templating has worked as a charm, and our web server is up and running!

How Spacelift Can Help You With Ansible Projects

Spacelift’s vibrant ecosystem and excellent GitOps flow can greatly assist you in managing and orchestrating Ansible. By introducing Spacelift on top of Ansible, you can then easily create custom workflows based on pull requests and apply any necessary compliance checks for your organization.

Another great advantage of using Spacelift is that you can manage different infrastructure tools like Ansible, Terraform, Pulumi, AWS CloudFormation, and even Kubernetes from the same place and combine their Stacks with building workflows across tools.

Our latest Ansible enhancements solve three of the biggest challenges engineers face when they are using Ansible:

  • Having a centralized place in which you can run your playbooks
  • Combining IaC with configuration management to create a single workflow
  • Getting insights into what ran and where

Provisioning, configuring, governing, and even orchestrating your containers can be performed with a single workflow, separating the elements into smaller chunks to identify issues more easily.

Would you like to see this in action – or just want a tl;dr? Check out this video I put together showing you Spacelift’s new Ansible functionality:

ansible product video thumbnail

If you want to learn more about using Spacelift with Ansible, check our documentation, read our Ansible guide, or book a demo with one of our engineers.

Key Points

This blog post deep-dived into the templating capabilities of Ansible. Ansible leverages Jinja2 to enable dynamic expressions and parametrization of files with variables, loops, conditions, and more. We discussed the characteristics and syntax of Jinja2, and we saw various templating examples. Finally, we reviewed a playbook that uses the template module to produce a configuration file for a web server. 

Thank you for reading, and I hope you enjoyed this as much as I did.

Manage Ansible Better with Spacelift

Managing large-scale playbook execution is hard. Spacelift enables you to automate Ansible playbook execution with visibility and control over resources, and seamlessly link provisioning and configuration workflows.

Learn more

The Practitioner’s Guide to Scaling Infrastructure as Code

Transform your IaC management to scale

securely, efficiently, and productively

into the future.

ebook global banner
Share your data and download the guide