A Complete Introduction to Using Ansible Templates

In this post, I’m going to tell you about Ansible templates, but first let’s talk about Ansible itself. It’s a suite of open-source infrastructure-as-code tool that includes software provisioning, configuration management, and functionality for application deployment. After installing it on a Linux machine, you can perform common tasks like copying and fetching files with a single command. You can even perform these tasks on connected Linux machines if Ansible isn’t installed on them. 

What Is an Ansible Template?

With Ansible templates, you can incorporate variables, loops, and conditionals into your configurations or scripts. By leveraging these programming constructs, you don’t have to hard-code values directly into your templates. Instead, you can define variables that can be easily modified or customized for different scenarios. 

Ansible’s template engine uses the popular Python templating language Jinja2. You’ll learn more about it in the next section. After that, I’ll show you how to create Ansible templates using Jinja2. 

What Type of Template Format Does Ansible Use?

As mentioned in the previous section, you can create Ansible templates using the templating language Jinja2. It’s a Python language that allows you to define variables using {{}} and give expressions using {% %}. You can also use “if else” statements and “for” loops in Jinja2.

Below is an example of a dynamic html file created using Jinja2. 

{% extends “index.html" %}

{% block body %}

<ol>

{% for book in books %}

<li><a href="{{ book.url }}">{{ book.name }}</a></li>

{% endfor %}

</ol>

{% endblock %}

Here, we have to provide an array of objects containing books (as in the code above) as shown below. 

[

  { name:'Ansible Basics', url: 'bit.ly/12Qhj78' },

  { name:'Ansible Intermediate', url: 'bit.ly/15Qhj78' },

  { name:'Ansible Advanced', url: 'bit.ly/12Ahj78' }

]

The `for` loop in the above Jinja2 script will run and convert it into list of items containing books. When we run the above Jinja2 script, we’ll get the output below. 

<body>

  <ol>

    <li><ahref="bit.ly/12Qhj78">Ansible Basics</a></li>

    <li><ahref="bit.ly/15Qhj78">Ansible Intermediate</a></li>

    <li><ahref="bit.ly/12Ahj78">Ansible Advanced</a></li>

  </ol>

</body>

Notice that we can pass an array of objects of any length, and it will be converted accordingly. We can use Jinja2 to perform automated DevOps tasks too, in which the playbook content will be replaced dynamically. We’ll cover that in the next section. 

Ansible Template Examples

To use Ansible templates, you need to use the template module in the playbook. It’s similar to the copy module, which transfers files to local or remote hosts.  

You need a templates folder where all template files will be stored. The templates folder should be the same as the playbook as in the structure below. 

├── templates

│   └── app.conf.j2

└── copy.yml

Notice that the file has an extension of j2, which means it’s a Jinja2 file. (You can hard code things in the template file, but that’s not advisable.) 

Next, I’ll show you a simple example where we copy a configuration file from the template to a destination on the local host. 

Basic Example

The `app.conf.j2` file will contain the below code. It includes an Ansible template variable of `env`. Below that are IP address and username variables, which will be taken from the keywords ansible_host and ansible_user. 

env = {{ env }}

ip = {{ ansible_host }}

user = {{ ansible_user }}

A simple `copy.yml` file is below. It includes the required name variable, hosts, and connection. After that is the `vars` that includes `env`, which can have any value. This value, which in this example is “production,” will be replaced in the `my_conf.j2` file. 

Next are the tasks, which again require a name. After that, we use the `template` module. For the `src`, we have to add our template file name, `app.conf.j2`. Notice that we don’t need to provide the exact path with the templates folder. The destination can be anything on the local host. And finally, we put the `app.conf` file in the `/etc` directory. 

# copy.yml

- name: copy a configuration file

  hosts: localhost

  connection: local

  vars:

     env: production

  tasks:

    - name: copy my_conf to the home directory using template 

      template:

        src: app.conf.j2

        dest: /etc/app.conf

We can run the playbook with the following command in the terminal: 

ansible-playbook copy.yml

This command will create a file named `app.conf` in the `/etc` directory. The content for it will be created dynamically, and if you’ll see this if you use the cat command: 

cat /etc/app.conf

env = production

ip = 13.234.45.71

user = naben

If you run the playbook with the template in a different system, you will get different results. 

Advanced Example

For a more advanced example, we’ll modify the basic example above and copy the `app.conf` file on multiple remote hosts. Additionally, we’ll copy another file, `app-version`, to all remote hosts. 

First, we’ll create an `app-version.j2` file in the templates folder and add the content below to it. 

App-Version {{ app_version }}

Next, we’ll update the `copy.yml` file as shown below. In the `hosts` is `remoteservers`, a group containing the list of remote hosts. In `tasks`, `become` means that remote servers will copy the file as the root user. 

We’re also adding a new `app_version` in `vars`, which we’re keeping as 12.2.3. This `app_version` will be used in the `app-version.j2` file. Finally, we have a `loop` that includes both files. We also modified the `src` and `dest` to use the item, which will be replaced by the loop files. 

# copy.yml

- name: copy two configuration files in remote hosts

  hosts: remoteservers

  vars:

     env: production

     app_version: 12.2.3

  tasks:

    - name: copy two configuration files in remote hosts using templates 

      become: true

      template:

        src: "{{ item }}.j2"

        dest: "/etc/{{ item }}"

      loop:

         - app.conf

         - app-version

We can run the playbook by entering command below in the terminal. 

ansible-playbook copy.yml

In all the remote hosts, it will create two files in the `/etc` folder called `app.conf` and `app-version`

ls -lrt /etc/app*

-rw-r--r-- 1 root root 59 Jan 26 07:18 /etc/app.conf

-rw-r--r-- 1 root root 12 Jan 26 07:18 /etc/app.version

To check the content of the `app.conf` file, use the cat command again to see the content below.

cat /etc/app.conf

env = production

ip = 13.234.45.71

user = naben

You can also check the content of the `app-version` file with the cat command.  

cat /etc/app-version

App-Version 12.2.3

NetBox as the Source of Truth

A real production environment will include routers, switches, and hosts. Sometimes you’ll need to add a configuration file to all these devices. Using a single source of truth is an excellent way to collect all the necessary details about these devices. It’s extremely cumbersome to go to each device and get its hostname, IP address, and so on. 

NetBox is one of the most popular solutions. It stores all data about these devices, you can easily access it using API endpoints, and it integrates well with Ansible. You can create a `netbox_inventory.yml` file to extract all device data in the Ansible playbook. You can learn more on the awesome Ansible blog and an accompanying YouTube tutorial.  

Conclusion

In this post, you learned about Ansible templates and Ansible’s template module. To learn more about getting started with Ansible for network automation, register for our webinar on June 30.

Just remember that in real production projects that include a lot of devices, you’re going to need a single source of truth for all these devices. NetBox is surely among the best, and you can try out a public demo if you’d like to jump right in. To try a hosted version of NetBox for two weeks, register for a free trial

This post was written by Nabendu Biswas. Nabendu has been working in the software industry for the past 15 years, starting as a C++ developer, then moving on to databases. For the past six years he’s been working as a web-developer working in the JavaScript ecosystem, and developing web-apps in ReactJS, NodeJS, GraphQL. He loves to blog about what he learns and what he’s up to.

Share the Post:

Related Posts