Ansible template Module Tutorial + Examples


Percy Grunwald's Profile Picture

Written by Percy Grunwald

— Last Updated August 2, 2020

What does the Ansible template module do?

Ansible’s template module transfers templated files to remote hosts. It works similarly to the copy module, but with 2 major differences:

  1. template looks for templates in ./templates/ when you supply a relative path for src (instead of ./files/ for copy)
  2. You can use the jinja2 templating language in your files, which will be templated out separately for each remote host

The template module is extremely useful for doing machine-specific configuration based on Ansible variables.

Given the following file structure:

.
├── ansible.cfg
├── templates
│   └── my_app.conf.j2
└── playbook.yml

With ./templates/my_app.conf.j2 containing:

local_ip = {{ ansible_default_ipv4["address"] }}
local_user = {{ ansible_user }}

Running the following task in playbook.yml:

- name: install my_app configuration file from template
  template:
    src: my_app.conf.j2
    dest: $HOME/my_app.conf

Produces the following on my Ubuntu 18.04 test host:

local_ip = 10.1.11.72
local_user = ubuntu

And the following on my Centos 7.5 test host:

local_ip = 10.1.11.62
local_user = centos

Ansible template Module Video Tutorial

If you prefer watching to reading, here’s a full video tutorial from the TopTechSkills YouTube channel covering a many of the points and examples from this article. Feel free to comment on this article or the video if you have any questions.

▶️ Watch on YouTube

Special variables available inside templates

The following variables are available inside templates in addition to all available Ansible variables:

  • ansible_managed - Usually put at the start of a templated file to indicate that the file is managed by Ansible and should not be modified manually. Configurable in ansible.cfg, please see the ansible_managed section of Ansible’s configuration guide for more details.
  • template_host - The node name of the host that executed the template (the local host).
  • template_uid - The numeric user id of the template file owner on the local host.
  • template_path- The absolute path to the template on the local host.
  • template_fullpath - Same as template_path in my experience.
  • template_run_date - The date the template was rendered.

Given the following file structure:

.
├── ansible.cfg
├── templates
│   └── special_variables.j2
└── playbook.yml

With ./templates/special_variables.j2 containing:

ansible_managed = "{{ ansible_managed }}"

template_host = "{{ template_host }}"

template_uid = "{{ template_uid }}"

template_path = "{{ template_path }}"

template_fullpath = "{{ template_fullpath }}"

template_run_date = "{{ template_run_date }}"

Will produce the following templated content (percy is my username on my MacBook Pro):

ansible_managed = "Ansible managed"

template_host = "Percys-MacBook-Pro.local"

template_uid = "percy"

template_path = "/path/to/ansible/templates/special_variables.j2"

template_fullpath = "/path/to/ansible/templates/special_variables.j2"

template_run_date = "2018-12-28 15:24:05.185182"

Examples

How to template a file onto a remote host

Set the src parameter to a jinja2 template in your ./templates/ directory and dest to the location on the remote host.

- name: template file to remote host
  template:
    src: my_app.conf.j2
    dest: $HOME/my_app.conf

How to set ownership and file permissions when templating a file onto a remote host

The owner, group and mode parameters give you fine control over the ownership and file permissions of the file(s) created by the template module. Remember that you will need become: true if you are setting the owner or group to values that don’t match the ansible_user.

The mode parameter accepts the file permissions setting in the following ways:

  • An octal number: e.g. 0644, 0600
  • Symbolic mode format: e.g. u=rw,g=r,o=r (where u is the owner, g is the group, and o is others). The permissions are r for read, w for write and x for execute.

With octal mode:

- name: template with ownership and octal mode
  template:
    src: my_app.conf.j2
    dest: $HOME/my_app.conf
    owner: "{{ ansible_user }}"
    group: "{{ ansible_user }}"
    mode: 0644

With symbolic mode:

- name: template with ownership and symbolic mode
  template:
    src: my_app.conf.j2
    dest: $HOME/my_app.conf
    owner: "{{ ansible_user }}"
    group: "{{ ansible_user }}"
    mode: u=rw,g=r,o=r

Setting a different owner with become: true:

- name: template with different owner (become required)
  template:
    src: my_app.conf.j2
    dest: /etc/my_app.conf
    owner: root
    group: root
    mode: 0644
  become: true

How to test that a file is valid before templating onto the remote host

Use the validate parameter to check whether a file is valid on the remote host before templating the file into place. This parameter is highly recommended if you have some program that can confirm the whether a target file is valid.

%s in the validate string refers to the file to be checked.

How to check the validity of a sudoers file before templating

The /usr/sbin/visudo -cf command checks the validity of a sudoers file:

- name: template /etc/sudoers with validation
  template:
    src: sudoers.j2
    dest: /etc/sudoers
    owner: root
    group: root
    mode: 0400
    validate: /usr/sbin/visudo -cf %s
  become: true

How to check the validity of an nginx configuration file before templating

The /usr/bin/nginx -t -c command checks the validity of an nginx configuration file:

- name: template /etc/nginx/nginx.conf with validation
  template:
    src: nginx.conf.j2
    dest: /etc/nginx/nginx.conf
    owner: root
    group: root
    mode: 0644
    validate: /usr/bin/nginx -t -c %s
  become: true

How to backup a file if template needs to overwrite it

By default, the template module will overwrite a file if it already exists on the remote host. Use the backup parameter if you want to backup the file before it is overwritten:

- name: template file to remote host with backup
  template:
    src: my_app.conf.j2
    dest: $HOME/my_app.conf
    backup: true

With backup: true, template will make a backup and append the timestamp before overwriting the file:

.
├── my_app.conf
└── my_app.conf.26507.2018-12-28@06:39:29~

How to template multiple files with a loop

Use the loop keyword to template multiple files. Remember that you can use the loop variables in your template file as well!

In the example below I’d like to create a .gitconfig for each user on the system. Here’s the template:

# templates/gitconfig.j2

[user]
  name = {{ user.name }}
  username = {{ user.username }}
  email = {{ user.username }}@example.com

[core]
  excludesfile = /home/{{ user.username }}/.gitignore

In the template we reference the user variable, which is going to come from each item in a loop. We can template this into each user’s home directory with the following task:

- name: create .gitconfig for each user
  template:
    src: .gitconfig.j2
    dest: "/home/{{ user.username }}/.gitconfig"
    owner: "{{ user.username }}"
    group: "{{ user.username }}"
    mode: 0644
  become: true
  loop:
    - name: John Smith
      username: jsmith
    - name: Jane Doe
      username: jdoe
  loop_control:
    loop_var: user

Notice that we’ve used the loop_control and loop_var directives to change the loop variable to user instead of item.

How to capture template module output

Use the register keyword to capture the output of the template module.

- name: template file to remote host
  template:
    src: my_app.conf.j2
    dest: $HOME/my_app.conf
  register: template_output

- debug: var=template_output

The debug task above will output the following:

ok: [123.123.123.123] => {
    "template_output": {
        "changed": true,
        "checksum": "120ec4bdd3cea15350829527ecbdb59604fafa11",
        "dest": "/home/ubuntu/my_app.conf",
        "diff": [],
        "failed": false,
        "gid": 1000,
        "group": "ubuntu",
        "md5sum": "62ca3280a599503f968d8ba8fa2fefd8",
        "mode": "0664",
        "owner": "ubuntu",
        "size": 43,
        "src": "/home/ubuntu/.ansible/tmp/ansible-tmp-1545979819.76-238399833013342/source",
        "state": "file",
        "uid": 1000
    }
}

How to capture template module output from a loop

When using the template module in a loop, the output of each item in the loop will go into the results key of the registered variable.

- name: template multiple file to remote host
  template:
    src: "{{ item }}.j2"
    dest: "$HOME/{{ item }}"
  register: template_output
  loop:
    - my_app.conf
    - welcome_message

- debug: var=template_output
ok: [54.206.30.148] => {
    "template_output": {
        "changed": true,
        "msg": "All items completed",
        "results": [
            {
                ...
                "changed": true,
                "checksum": "120ec4bdd3cea15350829527ecbdb59604fafa11",
                "dest": "/home/ubuntu/my_app.conf",
                "diff": [],
                "failed": false,
                "gid": 1000,
                "group": "ubuntu",
                "invocation": {
                    "module_args": {...},
                "item": "my_app.conf",
                "md5sum": "62ca3280a599503f968d8ba8fa2fefd8",
                "mode": "0664",
                "owner": "ubuntu",
                "size": 43,
                "src": "/home/ubuntu/.ansible/tmp/ansible-tmp-1545979954.53-190341758633466/source",
                "state": "file",
                "uid": 1000
            },
            {
                ...
                "changed": true,
                "checksum": "f76f29f91d6087dd008f4602433f178c73f7d91b",
                "dest": "/home/ubuntu/welcome_message",
                "diff": [],
                "failed": false,
                "gid": 1000,
                "group": "ubuntu",
                "invocation": {...},
                "item": "welcome_message",
                "md5sum": "2d1c4c3bea2435c5dbe4cebcfb40bfe9",
                "mode": "0664",
                "owner": "ubuntu",
                "size": 15,
                "src": "/home/ubuntu/.ansible/tmp/ansible-tmp-1545979956.57-220178044460544/source",
                "state": "file",
                "uid": 1000
            }
        ]
    }
}

Further reading

Ansible template Module on Ansible Docs

Comment & Share