Table of Contents
Go from Ansible beginner to Ansible pro with this full video course.
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:
templatelooks for templates in./templates/when you supply a relative path forsrc(instead of./files/forcopy)- You can use the
jinja2templating 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.
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 inansible.cfg, please see theansible_managedsection 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 astemplate_pathin 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(whereuis the owner,gis the group, andois others). The permissions arerforread,wfor write andxfor 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

