AnsibleXVII · Templates and Jinja2Templates and Jinja2
The template module in production
What you'll learn
- Treat mode, owner and group as part of the desired state rather than an afterthought
- Read the template module attributes to know what check mode and diff mode support
- Choose when backup and force are worth setting
- Explain why the replacement is atomic and what that protects against
Prerequisites
Verified against ansible-core 2.21.x · ansible (community package) 14.x · Python (controller) 3.12+ · ansible-lint 26.x · Molecule 26.x · Ubuntu 24.04 LTS · Debian 12 (Bookworm) · RHEL / Rocky / AlmaLinux 9.x · 2026-08-11
ansible-doc ansible.builtin.template lists around two dozen
parameters. About six of them decide whether the task is safe to run in
production. This lesson is those six, plus the attributes block, which
tells you what the module promises about check mode and diff mode and is
the part almost nobody reads.
The production shape
- name: Deploy the application configuration
ansible.builtin.template:
src: billing.conf.j2
dest: /etc/billing/billing.conf
owner: root
group: billing
mode: '0640'
backup: true
validate: /usr/local/bin/billing-check --config %s
notify: billing config changedmode, owner, group are the desired state, not decoration
A configuration file has permissions, and those permissions are part of what the file is. Leaving them off does not mean “keep whatever is there” — it means something more subtle and worse.
From the module documentation, verified on 2.21.3:
If
modeis not specified and the destination filesystem object does not exist, the defaultumaskon the system will be used when setting the mode for the newly created filesystem object.If
modeis not specified and the destination filesystem object does exist, the mode of the existing filesystem object will be used.
Read those two sentences together and the failure mode falls out. On a
host where the file already exists, the existing mode is preserved —
including a wrong one. On a host where it does not, the mode comes from
the remote user’s umask, which is an environment property you do not
control and which can differ between hosts in the same fleet.
So a task with no mode produces a file whose permissions depend on
whether the host had been configured before, and the fleet ends up
with two populations. That is not a hypothetical: the documentation
points at CVE-2020-1736 for the case where this created world-readable
files containing secrets.
The attributes block: what --check and --diff actually promise
Every module documents what it supports. For template on 2.21.3:
$ ansible-doc ansible.builtin.templateATTRIBUTES:
action:
description: Indicates this has a corresponding action plugin so some parts of the
options can be executed on the controller
support: full
check_mode:
description: Can run in check_mode and return changed status prediction without modifying
target, if not supported the action will be skipped.
support: full
diff_mode:
description: Will return details on what has changed (or possibly needs changing in
check_mode), when in diff mode
support: full
safe_file_operations:
description: Uses Ansible's strict file operation functions to ensure proper permissions
and avoid data corruption
support: full
vault:
description: Can automatically decrypt Ansible vaulted files
support: fullFour full supports, each with an operational consequence:
check_mode: full — --check genuinely predicts whether the file
would change, rather than skipping the task. This is the difference
between a dry run that tells you something and one that tells you
nothing. Contrast command and shell, which have no check-mode
support and are simply skipped, making a --check of a shell-based play
worthless.
diff_mode: full — --diff shows the line-by-line difference
between what is on the host and what would be written. Combined with
--check, that is a review of the actual change, per host, before
anything is touched. It is the single most useful habit in this part.
safe_file_operations: full — the replacement is atomic. A reader
of the file sees either the old content or the new content, never a
partial write, and never a zero-length file because the transfer was
interrupted.
vault: full — a template source that is itself vault-encrypted is
decrypted transparently.
backup, force, newline_sequence
backup: true writes a timestamped copy of the previous file next
to the original before replacing it. Verified defaults on 2.21.3:
backup is false.
It costs a file per change and buys a fast local rollback. Worth it on files where a bad change is expensive and the previous content is not trivially reproducible. Two caveats: the backups accumulate and nothing cleans them up, and — a genuine gotcha covered in the validate lesson — the backup is taken before validation, so a validation failure can leave a backup behind despite the destination being unchanged.
force: false (the default is true) changes the module from
“maintain this file” to “create this file if it does not exist”. Every
subsequent run leaves an existing file alone regardless of content.
That is the right choice for a file the machine or an operator is
expected to own after first boot, and it is a trap everywhere else: with
force: false the task reports ok forever, drift is invisible, and
the configuration in Git has no relationship to the configuration on the
host. If you find yourself reaching for it to stop Ansible fighting
someone else over a file, the answer is usually a drop-in file instead —
which is lesson 7 of this part.
newline_sequence defaults to \n, with \r and \r\n as the
other choices. Relevant when generating a file consumed by something
that requires CRLF. Rarely needed on Linux, and worth knowing exists
before you spend an hour on a parser that rejects your file.
ansible-playbook -i inventories/prod site.yml \
--limit webservers \
--check --diffKnowledge check
Knowledge check · 4 questions
Q1. A template task omits mode. Half the fleet already had the file; half did not. What are the resulting permissions?
Q2. What does check_mode: full on the template module guarantee, and what does it not?
Q3. Which statements about the template module are accurate? Select all that apply.
Q4. Running a play with --diff is safe regardless of what the templates contain, because check mode makes no changes.
Passing score: 75%. Answers are checked in this browser.