When managing configuration files with Ansible, a common requirement is to create files from templates but prevent overwriting existing files. The default behavior of Ansible's template module will overwrite the destination file every time the playbook runs, which might not be desirable for certain configuration files.
The template module fundamentally works by:
- Processing Jinja2 templates
- Copying the rendered content to destination
- Overwriting by default (similar to copy module)
- Setting proper file permissions
The most straightforward solution is to use the creates
parameter which makes the task idempotent:
tasks:
- template:
src: somefile.j2
dest: /etc/somefile.conf
creates: /etc/somefile.conf
For more complex scenarios, you might want to combine with the stat
module:
tasks:
- name: Check if file exists
stat:
path: /etc/somefile.conf
register: file_status
- name: Apply template only if file doesn't exist
template:
src: somefile.j2
dest: /etc/somefile.conf
when: not file_status.stat.exists
For production environments, consider these additional safeguards:
tasks:
- name: Safe template application
template:
src: somefile.j2
dest: /etc/somefile.conf
backup: yes
validate: '/usr/sbin/validator %s'
when: not file_status.stat.exists
The creates
approach is more efficient than using stat
+ conditional, as it:
- Reduces module executions
- Minimizes playbook complexity
- Maintains better readability
When automating server configurations with Ansible, we often face situations where we want to create configuration files from templates, but only if they don't already exist on the target system. This is particularly important when:
- Preserving manual configuration changes
- Maintaining idempotency in playbooks
- Avoiding overwriting user-modified files
Ansible provides several approaches to handle this scenario:
# Method 1: Using 'creates' parameter
- template:
src: somefile.j2
dest: /etc/somefile.conf
creates: /etc/somefile.conf # Won't run if file exists
# Method 2: Combining with stat module
- stat:
path: /etc/somefile.conf
register: config_status
- template:
src: somefile.j2
dest: /etc/somefile.conf
when: not config_status.stat.exists
For more complex scenarios, consider this pattern:
- name: Check if config file exists
stat:
path: "{{ config_file_path }}"
register: config_check
- name: Create initial config from template
template:
src: "templates/{{ config_template }}.j2"
dest: "{{ config_file_path }}"
owner: root
group: root
mode: '0644'
when: not config_check.stat.exists
tags:
- initial-config
For production environments, you might want to add these safeguards:
- name: Verify template deployment
assert:
that:
- config_check.stat.exists and config_check.stat.size > 0
msg: "Configuration file was not created properly"
when: not config_check.stat.exists
When dealing with multiple files, this optimized approach reduces playbook runtime:
- name: Gather existing config files
find:
paths: /etc
patterns: "*.conf"
register: existing_configs
- name: Deploy missing config templates
template:
src: "templates/{{ item }}.j2"
dest: "/etc/{{ item }}.conf"
loop: "{{ config_templates }}"
when: "'{{ item }}.conf' not in existing_configs.files|map(attribute='path')|list"